From 2c29d9baa1f93bbf3ac33e1226ec208e4f0daa2a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 10 Apr 2026 21:40:06 +0000
Subject: [PATCH 1/5] Initial plan
From f75d75c7e041d12a055aa558ab3116c98b45e028 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 10 Apr 2026 22:30:52 +0000
Subject: [PATCH 2/5] Fix XmlSerializer to handle empty/self-closing XmlElement
members correctly
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/a1d8fdd4-0c08-49e8-9258-3018c90b29f1
Co-authored-by: StephenMolloy <19562826+StephenMolloy@users.noreply.github.com>
---
.../Xml/Serialization/XmlSerializationReader.cs | 5 +++++
.../XmlSerializer/XmlSerializerTests.RuntimeOnly.cs | 11 +++++++++++
.../tests/SerializationTypes.cs | 6 ++++++
3 files changed, 22 insertions(+)
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 79741d5e849316..fa3ea9659737a2 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
@@ -905,6 +905,11 @@ protected XmlQualifiedName ReadElementQualifiedName()
if (wrapped)
{
if (ReadNull()) return null;
+ if (_r.IsEmptyElement)
+ {
+ _r.Skip();
+ return null;
+ }
_r.ReadStartElement();
_r.MoveToContent();
if (_r.NodeType != XmlNodeType.EndElement)
diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
index 5e57f747f5ce65..a18282d23034bb 100644
--- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
+++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
@@ -3666,4 +3666,15 @@ public static void SerializePrimitiveXmlTextAttributeOnDerivedClass()
var actual = SerializeAndDeserialize(value, "5");
Assert.Equal(value.Number, actual.Number);
}
+
+ [Theory]
+ [InlineData(@"text
Test", "Test")]
+ [InlineData(@"Test", "Test")]
+ [InlineData(@"Test", "Test")]
+ public static void Xml_XmlElementMember_EmptyElement_SiblingNotConsumed(string xml, string expectedName)
+ {
+ var serializer = new XmlSerializer(typeof(TypeWithXmlElementMemberAndSibling));
+ TypeWithXmlElementMemberAndSibling obj = (TypeWithXmlElementMemberAndSibling)serializer.Deserialize(new StringReader(xml));
+ Assert.Equal(expectedName, obj.Name);
+ }
}
diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
index b0835270f372f4..5b95276d5a6608 100644
--- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
+++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
@@ -1766,3 +1766,9 @@ public void ReadXml(XmlReader reader)
reader.ReadEndElement();
}
}
+
+public class TypeWithXmlElementMemberAndSibling
+{
+ public XmlElement Description { get; set; }
+ public string Name { get; set; }
+}
From 43cb1bdc664e9232e49e0c4c891ef22bac3ddb76 Mon Sep 17 00:00:00 2001
From: Steve Molloy
Date: Tue, 28 Apr 2026 10:11:31 -0700
Subject: [PATCH 3/5] Add AppContext switch for legacy empty XML element
deserialization support
---
.../src/System/Xml/Core/LocalAppContextSwitches.cs | 10 ++++++++++
.../System/Xml/Serialization/XmlSerializationReader.cs | 2 +-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs
index b41f05be289cce..fa0194f1b84bfe 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs
@@ -89,5 +89,15 @@ public static bool IgnoreObsoleteMembers
return SwitchesHelpers.GetCachedSwitchValue("Switch.System.Xml.IgnoreObsoleteMembers", ref s_ignoreObsoleteMembers);
}
}
+
+ private static int s_useLegacyEmptyXmlElementDeserialization;
+ public static bool UseLegacyEmptyXmlElementDeserialization
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return SwitchesHelpers.GetCachedSwitchValue("Switch.System.Xml.UseLegacyEmptyXmlElementDeserialization", ref s_useLegacyEmptyXmlElementDeserialization);
+ }
+ }
}
}
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 fa3ea9659737a2..2b727f317add58 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
@@ -905,7 +905,7 @@ protected XmlQualifiedName ReadElementQualifiedName()
if (wrapped)
{
if (ReadNull()) return null;
- if (_r.IsEmptyElement)
+ if (!LocalAppContextSwitches.UseLegacyEmptyXmlElementDeserialization && _r.IsEmptyElement)
{
_r.Skip();
return null;
From 31c1eb0e9e18060e4fdfc1d38790f31516b26768 Mon Sep 17 00:00:00 2001
From: Steve Molloy
Date: Tue, 28 Apr 2026 15:17:14 -0700
Subject: [PATCH 4/5] Enhance XmlSerializer tests for empty XML element
deserialization with additional scenarios
---
.../XmlSerializerTests.RuntimeOnly.cs | 33 ++++++++++++++-----
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
index a18282d23034bb..07f0e6816fb2d0 100644
--- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
+++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs
@@ -3668,13 +3668,30 @@ public static void SerializePrimitiveXmlTextAttributeOnDerivedClass()
}
[Theory]
- [InlineData(@"text
Test", "Test")]
- [InlineData(@"Test", "Test")]
- [InlineData(@"Test", "Test")]
- public static void Xml_XmlElementMember_EmptyElement_SiblingNotConsumed(string xml, string expectedName)
- {
- var serializer = new XmlSerializer(typeof(TypeWithXmlElementMemberAndSibling));
- TypeWithXmlElementMemberAndSibling obj = (TypeWithXmlElementMemberAndSibling)serializer.Deserialize(new StringReader(xml));
- Assert.Equal(expectedName, obj.Name);
+ [InlineData(@"text
Test", "Test", true, "p", "text")]
+ [InlineData(@"Test", "Test", false, null, null)]
+ [InlineData(@"Test", "Test", false, null, null, false)]
+ [InlineData(@"Test", "Test", false, null, null, false)]
+ [InlineData(@"text
Test", "Test", true, "p", "text", true)]
+ [InlineData(@"Test", null, true, "Name", "Test", true)]
+ [InlineData(@"Test", "Test", false, null, null, true)]
+ public static void Xml_XmlElementMember_EmptyElement_SiblingNotConsumed(string xml, string? expectedName, bool expectDescription, string? expectedDescriptionName, string? expectedDescriptionInnerXml, bool? compatSwitch = null)
+ {
+ using (var appContextScope = compatSwitch.HasValue ? new XmlSerializerAppContextSwitchScope("Switch.System.Xml.UseLegacyEmptyXmlElementDeserialization", compatSwitch.Value) : null)
+ {
+ var serializer = new XmlSerializer(typeof(TypeWithXmlElementMemberAndSibling));
+ TypeWithXmlElementMemberAndSibling obj = (TypeWithXmlElementMemberAndSibling)serializer.Deserialize(new StringReader(xml));
+ Assert.Equal(expectedName, obj.Name);
+ if (expectDescription)
+ {
+ Assert.NotNull(obj.Description);
+ Assert.Equal(expectedDescriptionName, obj.Description.Name);
+ Assert.Equal(expectedDescriptionInnerXml, obj.Description.InnerXml);
+ }
+ else
+ {
+ Assert.Null(obj.Description);
+ }
+ }
}
}
From c2effe8b9c385ed2cf33ed9bc4d7cf3ac6acd163 Mon Sep 17 00:00:00 2001
From: Steve Molloy
Date: Tue, 28 Apr 2026 15:35:52 -0700
Subject: [PATCH 5/5] Move new type used in test to runtime-only.
---
.../tests/SerializationTypes.RuntimeOnly.cs | 6 ++++++
.../tests/SerializationTypes.cs | 6 ------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs
index cfd07e6603c24b..6303b4bf9f431f 100644
--- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs
+++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs
@@ -836,6 +836,12 @@ public WithListOfXElement(bool init)
}
}
+ public class TypeWithXmlElementMemberAndSibling
+ {
+ public XmlElement Description { get; set; }
+ public string Name { get; set; }
+ }
+
public class BaseType
{
public virtual string Name1 { get; set; }
diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
index 5b95276d5a6608..b0835270f372f4 100644
--- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
+++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs
@@ -1766,9 +1766,3 @@ public void ReadXml(XmlReader reader)
reader.ReadEndElement();
}
}
-
-public class TypeWithXmlElementMemberAndSibling
-{
- public XmlElement Description { get; set; }
- public string Name { get; set; }
-}