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