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; } -}