From 8ac5ebb17ea34e6aa0028bd08fc9d38b0a8b7d7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 07:12:25 +0000 Subject: [PATCH 1/3] Initial plan From 195a757d93990c4e1fdbf8d0d5d6890a9d609be9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:01:52 +0000 Subject: [PATCH 2/3] Fix XmlElement deserialization for empty elements Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com> --- .../Serialization/XmlSerializationReader.cs | 32 ++++++-- .../XmlSerializationEmptyElementTests.cs | 76 +++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index 93e3889be7e24d..3a216d3eddf4f1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -893,15 +893,35 @@ protected XmlQualifiedName ReadElementQualifiedName() if (wrapped) { if (ReadNull()) return null; + + // Store element information before consuming it + string localName = _r.LocalName; + string namespaceURI = _r.NamespaceURI; + bool isEmpty = _r.IsEmptyElement; + _r.ReadStartElement(); - _r.MoveToContent(); - if (_r.NodeType != XmlNodeType.EndElement) - node = Document.ReadNode(_r); - while (_r.NodeType != XmlNodeType.EndElement) + + if (isEmpty) + { + // For empty elements, create an empty XmlElement + node = Document.CreateElement(localName, namespaceURI); + } + else { - UnknownNode(null); + _r.MoveToContent(); + if (_r.NodeType != XmlNodeType.EndElement) + node = Document.ReadNode(_r); + else + { + // Element is empty (no content between start and end tags) + node = Document.CreateElement(localName, namespaceURI); + } + while (_r.NodeType != XmlNodeType.EndElement) + { + UnknownNode(null); + } + _r.ReadEndElement(); } - _r.ReadEndElement(); } else { diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs new file mode 100644 index 00000000000000..85f3b3545c5d9e --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using Xunit; + +namespace System.Private.Xml.Tests +{ + public static class XmlSerializationEmptyElementTests + { + [Fact] + public static void XmlElement_EmptyElement_DoesNotConsumeSubsequentElements() + { + var serializer = new XmlSerializer(typeof(RootWithXmlElement)); + + // Test case with empty description element followed by name element + var xml = @"Test"; + var result = (RootWithXmlElement)serializer.Deserialize(new StringReader(xml)); + + // The Description should be an empty element (not null, not consuming the Name element) + Assert.NotNull(result.Description); + Assert.Equal("description", result.Description.LocalName); + Assert.True(string.IsNullOrEmpty(result.Description.InnerXml)); + Assert.True(result.Description.IsEmpty || result.Description.InnerXml == ""); + + // The Name should still be correctly deserialized + Assert.Equal("Test", result.Name); + } + + [Fact] + public static void XmlElement_NonEmptyElement_WorksCorrectly() + { + var serializer = new XmlSerializer(typeof(RootWithXmlElement)); + + // Test case with non-empty description element + var xml = @"

test

Test
"; + var result = (RootWithXmlElement)serializer.Deserialize(new StringReader(xml)); + + // The Description should contain the paragraph + Assert.NotNull(result.Description); + Assert.Equal("description", result.Description.LocalName); + Assert.Equal("

test

", result.Description.InnerXml); + + // The Name should still be correctly deserialized + Assert.Equal("Test", result.Name); + } + + [Fact] + public static void XmlElement_EmptyElement_BehaviorConsistentWithNonEmpty() + { + var serializer = new XmlSerializer(typeof(RootWithXmlElement)); + + // Empty element case + var xml1 = @"Test"; + var result1 = (RootWithXmlElement)serializer.Deserialize(new StringReader(xml1)); + + // Self-closing element case (equivalent to empty) + var xml2 = @"Test"; + var result2 = (RootWithXmlElement)serializer.Deserialize(new StringReader(xml2)); + + // Both should behave the same way + Assert.Equal(result1.Description.InnerXml, result2.Description.InnerXml); + Assert.Equal(result1.Name, result2.Name); + Assert.Equal("Test", result1.Name); + Assert.Equal("Test", result2.Name); + } + + public class RootWithXmlElement + { + public XmlElement Description { get; set; } + public string Name { get; set; } + } + } +} \ No newline at end of file From 6f9b65bcb37763b1e38aaee2e65597fac6b0491a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:04:09 +0000 Subject: [PATCH 3/3] Complete fix for XmlElement empty element deserialization with attribute support Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com> --- .../Serialization/XmlSerializationReader.cs | 27 ++++++++++++++++--- .../tests/System.Private.Xml.Tests.csproj | 1 + .../XmlSerializationEmptyElementTests.cs | 24 +++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index 3a216d3eddf4f1..ef70627286ee8f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -899,12 +899,28 @@ protected XmlQualifiedName ReadElementQualifiedName() string namespaceURI = _r.NamespaceURI; bool isEmpty = _r.IsEmptyElement; + // Store attributes if present + var attributes = new List<(string name, string namespaceUri, string value)>(); + if (_r.HasAttributes) + { + while (_r.MoveToNextAttribute()) + { + attributes.Add((_r.Name, _r.NamespaceURI, _r.Value)); + } + _r.MoveToElement(); + } + _r.ReadStartElement(); if (isEmpty) { - // For empty elements, create an empty XmlElement - node = Document.CreateElement(localName, namespaceURI); + // For empty elements, create an empty XmlElement with attributes + var element = Document.CreateElement(localName, namespaceURI); + foreach (var (name, nsUri, value) in attributes) + { + element.SetAttribute(name, nsUri, value); + } + node = element; } else { @@ -914,7 +930,12 @@ protected XmlQualifiedName ReadElementQualifiedName() else { // Element is empty (no content between start and end tags) - node = Document.CreateElement(localName, namespaceURI); + var element = Document.CreateElement(localName, namespaceURI); + foreach (var (name, nsUri, value) in attributes) + { + element.SetAttribute(name, nsUri, value); + } + node = element; } while (_r.NodeType != XmlNodeType.EndElement) { diff --git a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj index 1ce5192ef4ad2d..5464e6455e36d9 100644 --- a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj @@ -434,6 +434,7 @@ + diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs index 85f3b3545c5d9e..a32debca7e9113 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializationEmptyElementTests.cs @@ -65,6 +65,30 @@ public static void XmlElement_EmptyElement_BehaviorConsistentWithNonEmpty() Assert.Equal(result1.Name, result2.Name); Assert.Equal("Test", result1.Name); Assert.Equal("Test", result2.Name); + + // Both should have empty description elements + Assert.NotNull(result1.Description); + Assert.NotNull(result2.Description); + Assert.Equal("description", result1.Description.LocalName); + Assert.Equal("description", result2.Description.LocalName); + Assert.True(string.IsNullOrEmpty(result1.Description.InnerXml)); + Assert.True(string.IsNullOrEmpty(result2.Description.InnerXml)); + } + + [Fact] + public static void XmlElement_EmptyElement_ShouldNotBeNull() + { + var serializer = new XmlSerializer(typeof(RootWithXmlElement)); + + // Test that empty elements create XmlElement objects, not null + var xml = @"Test"; + var result = (RootWithXmlElement)serializer.Deserialize(new StringReader(xml)); + + // Description should not be null - it should be an empty XmlElement + Assert.NotNull(result.Description); + Assert.Equal("description", result.Description.LocalName); + Assert.Equal("", result.Description.InnerXml); + Assert.Equal("Test", result.Name); } public class RootWithXmlElement