From 589f49cfb2077ef966e6beedd5db4583857c089d Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 16 Jun 2026 15:20:13 -0700 Subject: [PATCH 1/5] Implement support for derived IXmlSerializable types in XmlSerializer --- .../ReflectionXmlSerializationReader.cs | 60 ++++++++++++++----- .../XmlSerializerTests.Internal.cs | 21 ------- .../tests/XmlSerializer/XmlSerializerTests.cs | 20 +++++++ 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 0c08561ba20ced..aec52ab03dc8a3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -975,34 +975,29 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM break; case TypeKind.Serializable: SerializableMapping sm = (SerializableMapping)element.Mapping; - // check to see if we need to do the derivation - bool flag = true; + bool isWrappedAny = !element.Any && IsWildcard(sm); + // Check to see if we need to do the derivation, i.e. the actual xsi:type + // refers to a type derived from the declared SerializableMapping type. if (sm.DerivedMappings != null) { XmlQualifiedName? tser = GetXsiType(); - if (tser == null || QNameEqual(tser, sm.XsiType!.Name, defaultNamespace)) + if (tser == null || QNameEqual(tser, sm.XsiType!.Name, sm.XsiType.Namespace)) { + value = ReadSerializable((IXmlSerializable)ReflectionCreateObject(sm.TypeDesc!.Type!)!, isWrappedAny); + } + else if (ReadDerivedSerializable(sm, sm, tser, isWrappedAny, out object? derivedValue)) + { + value = derivedValue; } else { - flag = false; + UnknownNode(null); } } - - if (flag) + else { - bool isWrappedAny = !element.Any && IsWildcard(sm); value = ReadSerializable((IXmlSerializable)ReflectionCreateObject(sm.TypeDesc!.Type!)!, isWrappedAny); } - - if (sm.DerivedMappings != null) - { - // https://github.com/dotnet/runtime/issues/1401: - // To Support SpecialMapping Types Having DerivedMappings - throw new NotImplementedException("sm.DerivedMappings != null"); - //WriteDerivedSerializable(sm, sm, source, isWrappedAny); - //WriteUnknownNode("UnknownNode", "null", null, true); - } break; default: throw new InvalidOperationException(SR.XmlInternalError); @@ -1028,6 +1023,39 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM return value; } + // Walks the SerializableMapping derivation tree looking for the mapping whose XsiType matches + // the supplied xsi:type. Mirrors XmlSerializationReaderILGen.WriteDerivedSerializable. Returns + // true and the deserialized object when a derived mapping matches; otherwise false. + private bool ReadDerivedSerializable(SerializableMapping head, SerializableMapping mapping, XmlQualifiedName? tser, bool isWrappedAny, out object? value) + { + for (SerializableMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) + { + if (tser == null || QNameEqual(tser, derived.XsiType!.Name, derived.XsiType.Namespace)) + { + if (derived.Type == null) + { + throw CreateMissingIXmlSerializableType(derived.XsiType!.Name, derived.XsiType.Namespace, head.Type!.FullName); + } + + if (!head.Type!.IsAssignableFrom(derived.Type)) + { + throw CreateBadDerivationException(derived.XsiType!.Name, derived.XsiType.Namespace, head.XsiType!.Name, head.XsiType.Namespace, derived.Type.FullName, head.Type.FullName); + } + + value = ReadSerializable((IXmlSerializable)ReflectionCreateObject(derived.TypeDesc!.Type!)!, isWrappedAny); + return true; + } + + if (ReadDerivedSerializable(head, derived, tser, isWrappedAny, out value)) + { + return true; + } + } + + value = null; + return false; + } + private XmlSerializationReadCallback CreateXmlSerializationReadCallback(TypeMapping mapping) { if (mapping is StructMapping structMapping) diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs index 733b5ae83c1952..0175b1685e90a0 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs @@ -32,27 +32,6 @@ public static void Xml_CustomDocumentWithXmlAttributesAsNodes() Assert.NotNull(actual); } - // Move this test to XmlSerializerTests.cs once #1401 is fixed for the ReflectionOnly serializer. - [Fact] - public static void Xml_DerivedIXmlSerializable() - { - var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = DateTime.Parse("Dec 31, 1999"), BoolValue = true }; - - var expectedXml = WithXmlHeader(@$""); - var fromBase = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableBaseClass), new Type[] { typeof(XmlSerializableDerivedClass) })); - Assert.Equal(dClass.AttributeString, fromBase.AttributeString); - Assert.Equal(dClass.DateTimeValue, fromBase.DateTimeValue); - Assert.Equal(dClass.BoolValue, fromBase.BoolValue); - - // Derived class does not apply XmlRoot attribute to force itself to be emitted with the base class element name, so update expected xml accordingly. - // Since we can't smartly emit xsi:type during serialization though, it is still there even though it isn't needed. - expectedXml = WithXmlHeader(@""); - var fromDerived = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableDerivedClass))); - Assert.Equal(dClass.AttributeString, fromDerived.AttributeString); - Assert.Equal(dClass.DateTimeValue, fromDerived.DateTimeValue); - Assert.Equal(dClass.BoolValue, fromDerived.BoolValue); - } - // Move this test to XmlSerializerTests.cs once #1402 is fixed for the ReflectionOnly serializer. // Actually, this test is already there, but it's commented out. Uncomment it once #1402 is fixed. // BTW, there are multiple (4?) places in the refelction reader where this issue is referenced, although diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 1451e7407d8148..532451d270918f 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -715,6 +715,26 @@ public static void Xml_ClassImplementingIXmlSerializable() Assert.True(ClassImplementingIXmlSerializable.WriteXmlInvoked); } + [Fact] + public static void Xml_DerivedIXmlSerializable() + { + var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = DateTime.Parse("Dec 31, 1999"), BoolValue = true }; + + var expectedXml = WithXmlHeader(@$""); + var fromBase = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableBaseClass), new Type[] { typeof(XmlSerializableDerivedClass) })); + Assert.Equal(dClass.AttributeString, fromBase.AttributeString); + Assert.Equal(dClass.DateTimeValue, fromBase.DateTimeValue); + Assert.Equal(dClass.BoolValue, fromBase.BoolValue); + + // Derived class does not apply XmlRoot attribute to force itself to be emitted with the base class element name, so update expected xml accordingly. + // Since we can't smartly emit xsi:type during serialization though, it is still there even though it isn't needed. + expectedXml = WithXmlHeader(@""); + var fromDerived = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableDerivedClass))); + Assert.Equal(dClass.AttributeString, fromDerived.AttributeString); + Assert.Equal(dClass.DateTimeValue, fromDerived.DateTimeValue); + Assert.Equal(dClass.BoolValue, fromDerived.BoolValue); + } + [Fact] public static void Xml_StructImplementingIXmlSerializableWithoutParameterlessConstructor() { From e33df836e14a3ae26e273ad1a0fd6c82ffd7204c Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Mon, 29 Jun 2026 18:26:22 -0700 Subject: [PATCH 2/5] Don't clobber member with null on unknown xsi:type for derived IXmlSerializable Mirror XmlSerializationReaderILGen in the reflection-based reader: when an unknown xsi:type is encountered for a serializable member with derived mappings, consume the node and perform choice/specified bookkeeping but skip the member value assignment so a pre-populated member is not overwritten with null. Adds a regression test that runs in both the ILGen and ReflectionOnly readers, and replaces a culture-dependent DateTime.Parse in the existing derived-IXmlSerializable test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ReflectionXmlSerializationReader.cs | 8 +++++ .../tests/XmlSerializer/XmlSerializerTests.cs | 36 ++++++++++++++++++- .../tests/SerializationTypes.cs | 14 ++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index aec52ab03dc8a3..9e13e0b03b158d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -991,7 +991,15 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM } else { + // The xsi:type matched neither the declared serializable type nor any + // known derived type. Mirror XmlSerializationReaderILGen, which emits + // only Reader.UnknownNode(null) here and leaves the member untouched + // rather than overwriting it with null. Still perform the choice and + // specified bookkeeping the ILGen reader does unconditionally. UnknownNode(null); + member?.ChoiceSource?.Invoke(element.Name); + member?.CheckSpecifiedSource?.Invoke(true); + return value; } } else diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 532451d270918f..a1597f409f40e6 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -718,7 +718,7 @@ public static void Xml_ClassImplementingIXmlSerializable() [Fact] public static void Xml_DerivedIXmlSerializable() { - var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = DateTime.Parse("Dec 31, 1999"), BoolValue = true }; + var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true }; var expectedXml = WithXmlHeader(@$""); var fromBase = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableBaseClass), new Type[] { typeof(XmlSerializableDerivedClass) })); @@ -735,6 +735,40 @@ public static void Xml_DerivedIXmlSerializable() Assert.Equal(dClass.BoolValue, fromDerived.BoolValue); } + [Fact] + public static void Xml_DerivedIXmlSerializable_UnknownXsiTypeDoesNotClobberMember() + { + var serializer = new XmlSerializer(typeof(XmlSerializableMemberWrapper), new Type[] { typeof(XmlSerializableDerivedClass) }); + + // Produce valid XML carrying a real xsi:type for the IXmlSerializable member, then swap the + // xsi:type to a name that matches neither the declared serializable type nor any known derived + // type. On this path the ILGen reader emits only Reader.UnknownNode(null) and leaves the member + // untouched; the reflection reader must behave identically rather than overwriting it with null. + var wrapper = new XmlSerializableMemberWrapper() + { + Member = new XmlSerializableDerivedClass() { AttributeString = "derived", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true } + }; + + string validXml; + using (var sw = new StringWriter()) + { + serializer.Serialize(sw, wrapper); + validXml = sw.ToString(); + } + + string unknownTypeXml = validXml.Replace(@"xsi:type=""DerivedIXmlSerializable""", @"xsi:type=""NonExistentDerivedType"""); + Assert.DoesNotContain(@"xsi:type=""DerivedIXmlSerializable""", unknownTypeXml); + + XmlSerializableMemberWrapper result; + using (var sr = new StringReader(unknownTypeXml)) + { + result = (XmlSerializableMemberWrapper)serializer.Deserialize(sr); + } + + Assert.NotNull(result.Member); + Assert.Equal(XmlSerializableMemberWrapper.PresetAttributeString, result.Member.AttributeString); + } + [Fact] public static void Xml_StructImplementingIXmlSerializableWithoutParameterlessConstructor() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index c21ad431ae729d..90fcd29d13de3c 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -1444,6 +1444,20 @@ public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer) public static new XmlQualifiedName GetMySchema(XmlSchemaSet xs) => GetSchemaForType(xs, DerivedName); } +public class XmlSerializableMemberWrapper +{ + public XmlSerializableMemberWrapper() + { + // Pre-populate the member so a regression that overwrites it with null when an + // unrecognized xsi:type is encountered can be detected. + Member = new XmlSerializableBaseClass() { AttributeString = PresetAttributeString }; + } + + public const string PresetAttributeString = "preset"; + + public XmlSerializableBaseClass Member { get; set; } +} + [XmlRootAttribute("PurchaseOrder", Namespace = "http://www.contoso1.com", IsNullable = false)] public class PurchaseOrder { From 04cd1bcefa146bbcc3612e6a1427f7b1f1a0ff6f Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 30 Jun 2026 09:42:30 -0700 Subject: [PATCH 3/5] Move derived IXmlSerializable tests to RuntimeOnly to avoid pregen interference The XmlSerializableMemberWrapper type lived in the shared SerializationTypes.cs, which is compiled into SerializableAssembly. The pregen-serializer test (SgenCommandTest) runs SGen over that assembly and compares output length against a checked-in golden file, so the added type changed the generated length. Move the derived IXmlSerializable tests and the wrapper type into XmlSerializerTests.RuntimeOnly.cs, which the pregen test project does not compile, so the tests still run in both the ILGen and ReflectionOnly readers without affecting SerializableAssembly or the golden file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../XmlSerializerTests.RuntimeOnly.cs | 68 +++++++++++++++++++ .../tests/XmlSerializer/XmlSerializerTests.cs | 54 --------------- .../tests/SerializationTypes.cs | 14 ---- 3 files changed, 68 insertions(+), 68 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 5e57f747f5ce65..10ac83cbc3f92b 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,72 @@ public static void SerializePrimitiveXmlTextAttributeOnDerivedClass() var actual = SerializeAndDeserialize(value, "5"); Assert.Equal(value.Number, actual.Number); } + + [Fact] + public static void Xml_DerivedIXmlSerializable() + { + var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true }; + + var expectedXml = WithXmlHeader(@$""); + var fromBase = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableBaseClass), new Type[] { typeof(XmlSerializableDerivedClass) })); + Assert.Equal(dClass.AttributeString, fromBase.AttributeString); + Assert.Equal(dClass.DateTimeValue, fromBase.DateTimeValue); + Assert.Equal(dClass.BoolValue, fromBase.BoolValue); + + // Derived class does not apply XmlRoot attribute to force itself to be emitted with the base class element name, so update expected xml accordingly. + // Since we can't smartly emit xsi:type during serialization though, it is still there even though it isn't needed. + expectedXml = WithXmlHeader(@""); + var fromDerived = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableDerivedClass))); + Assert.Equal(dClass.AttributeString, fromDerived.AttributeString); + Assert.Equal(dClass.DateTimeValue, fromDerived.DateTimeValue); + Assert.Equal(dClass.BoolValue, fromDerived.BoolValue); + } + + [Fact] + public static void Xml_DerivedIXmlSerializable_UnknownXsiTypeDoesNotClobberMember() + { + var serializer = new XmlSerializer(typeof(XmlSerializableMemberWrapper), new Type[] { typeof(XmlSerializableDerivedClass) }); + + // Produce valid XML carrying a real xsi:type for the IXmlSerializable member, then swap the + // xsi:type to a name that matches neither the declared serializable type nor any known derived + // type. On this path the ILGen reader emits only Reader.UnknownNode(null) and leaves the member + // untouched; the reflection reader must behave identically rather than overwriting it with null. + var wrapper = new XmlSerializableMemberWrapper() + { + Member = new XmlSerializableDerivedClass() { AttributeString = "derived", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true } + }; + + string validXml; + using (var sw = new StringWriter()) + { + serializer.Serialize(sw, wrapper); + validXml = sw.ToString(); + } + + string unknownTypeXml = validXml.Replace(@"xsi:type=""DerivedIXmlSerializable""", @"xsi:type=""NonExistentDerivedType"""); + Assert.DoesNotContain(@"xsi:type=""DerivedIXmlSerializable""", unknownTypeXml); + + XmlSerializableMemberWrapper result; + using (var sr = new StringReader(unknownTypeXml)) + { + result = (XmlSerializableMemberWrapper)serializer.Deserialize(sr); + } + + Assert.NotNull(result.Member); + Assert.Equal(XmlSerializableMemberWrapper.PresetAttributeString, result.Member.AttributeString); + } +} + +public class XmlSerializableMemberWrapper +{ + public XmlSerializableMemberWrapper() + { + // Pre-populate the member so a regression that overwrites it with null when an + // unrecognized xsi:type is encountered can be detected. + Member = new XmlSerializableBaseClass() { AttributeString = PresetAttributeString }; + } + + public const string PresetAttributeString = "preset"; + + public XmlSerializableBaseClass Member { get; set; } } diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index a1597f409f40e6..1451e7407d8148 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -715,60 +715,6 @@ public static void Xml_ClassImplementingIXmlSerializable() Assert.True(ClassImplementingIXmlSerializable.WriteXmlInvoked); } - [Fact] - public static void Xml_DerivedIXmlSerializable() - { - var dClass = new XmlSerializableDerivedClass() { AttributeString = "derivedIXmlSerTest", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true }; - - var expectedXml = WithXmlHeader(@$""); - var fromBase = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableBaseClass), new Type[] { typeof(XmlSerializableDerivedClass) })); - Assert.Equal(dClass.AttributeString, fromBase.AttributeString); - Assert.Equal(dClass.DateTimeValue, fromBase.DateTimeValue); - Assert.Equal(dClass.BoolValue, fromBase.BoolValue); - - // Derived class does not apply XmlRoot attribute to force itself to be emitted with the base class element name, so update expected xml accordingly. - // Since we can't smartly emit xsi:type during serialization though, it is still there even though it isn't needed. - expectedXml = WithXmlHeader(@""); - var fromDerived = SerializeAndDeserialize(dClass, expectedXml, () => new XmlSerializer(typeof(XmlSerializableDerivedClass))); - Assert.Equal(dClass.AttributeString, fromDerived.AttributeString); - Assert.Equal(dClass.DateTimeValue, fromDerived.DateTimeValue); - Assert.Equal(dClass.BoolValue, fromDerived.BoolValue); - } - - [Fact] - public static void Xml_DerivedIXmlSerializable_UnknownXsiTypeDoesNotClobberMember() - { - var serializer = new XmlSerializer(typeof(XmlSerializableMemberWrapper), new Type[] { typeof(XmlSerializableDerivedClass) }); - - // Produce valid XML carrying a real xsi:type for the IXmlSerializable member, then swap the - // xsi:type to a name that matches neither the declared serializable type nor any known derived - // type. On this path the ILGen reader emits only Reader.UnknownNode(null) and leaves the member - // untouched; the reflection reader must behave identically rather than overwriting it with null. - var wrapper = new XmlSerializableMemberWrapper() - { - Member = new XmlSerializableDerivedClass() { AttributeString = "derived", DateTimeValue = new DateTime(1999, 12, 31), BoolValue = true } - }; - - string validXml; - using (var sw = new StringWriter()) - { - serializer.Serialize(sw, wrapper); - validXml = sw.ToString(); - } - - string unknownTypeXml = validXml.Replace(@"xsi:type=""DerivedIXmlSerializable""", @"xsi:type=""NonExistentDerivedType"""); - Assert.DoesNotContain(@"xsi:type=""DerivedIXmlSerializable""", unknownTypeXml); - - XmlSerializableMemberWrapper result; - using (var sr = new StringReader(unknownTypeXml)) - { - result = (XmlSerializableMemberWrapper)serializer.Deserialize(sr); - } - - Assert.NotNull(result.Member); - Assert.Equal(XmlSerializableMemberWrapper.PresetAttributeString, result.Member.AttributeString); - } - [Fact] public static void Xml_StructImplementingIXmlSerializableWithoutParameterlessConstructor() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index 90fcd29d13de3c..c21ad431ae729d 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -1444,20 +1444,6 @@ public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer) public static new XmlQualifiedName GetMySchema(XmlSchemaSet xs) => GetSchemaForType(xs, DerivedName); } -public class XmlSerializableMemberWrapper -{ - public XmlSerializableMemberWrapper() - { - // Pre-populate the member so a regression that overwrites it with null when an - // unrecognized xsi:type is encountered can be detected. - Member = new XmlSerializableBaseClass() { AttributeString = PresetAttributeString }; - } - - public const string PresetAttributeString = "preset"; - - public XmlSerializableBaseClass Member { get; set; } -} - [XmlRootAttribute("PurchaseOrder", Namespace = "http://www.contoso1.com", IsNullable = false)] public class PurchaseOrder { From 25d4774d0ceb95ea08e34f7ed3330c5a44672c27 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 14 Jul 2026 18:11:20 -0700 Subject: [PATCH 4/5] Re-add test that got lost during merge conflict resolution Re-add test that got lost during merge conflict resolution --- .../tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs | 1 + 1 file changed, 1 insertion(+) 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 c3ab485ff513f0..5aef65d35f4280 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -3770,6 +3770,7 @@ public static void Xml_XmlElementMember_EmptyElement_SiblingNotConsumed(string x } } + [Fact] public static void Xml_TypeWithArrayLikeChoiceElement() { // Exercises an [XmlChoiceIdentifier] member where one of the choice element types is From 6ac5270107bb64292209f734ec197f81e6092203 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Mon, 27 Jul 2026 23:01:54 -0700 Subject: [PATCH 5/5] Merge conflict edits --- .../ReflectionXmlSerializationReader.cs | 2 +- .../tests/System.Private.Xml.Tests.csproj | 1 - .../XmlSerializer/XmlSerializerTests.Internal.cs | 14 -------------- 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index b8575710b73c9d..0ce9fa2d743c7c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -1037,7 +1037,7 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM // rather than overwriting it with null. Still perform the choice and // specified bookkeeping the ILGen reader does unconditionally. UnknownNode(null); - member?.ChoiceSource?.Invoke(element.Name); + member?.ChoiceSource?.Invoke(elementIndex); member?.CheckSpecifiedSource?.Invoke(true); return value; } 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 b36f101e0cacad..fd0263004573b2 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 @@ -439,7 +439,6 @@ - diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs deleted file mode 100644 index b98400c9fb22be..00000000000000 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Xml; -using System.Xml.Schema; -using System.Xml.Serialization; -using SerializationTypes; -using Xunit; - -public static partial class XmlSerializerTests -{ -}