From 7be982e52276933933a1237a38c846e9446a281c Mon Sep 17 00:00:00 2001 From: haltandcatchwater Date: Fri, 20 Mar 2026 13:11:35 -0700 Subject: [PATCH] Fix NullReferenceException in ReflectionXmlSerializationWriter for inherited ShouldSerialize methods ReflectionXmlSerializationWriter used BindingFlags.DeclaredOnly when looking up ShouldSerialize methods at two call sites, which prevented finding methods declared on base classes. This caused a NullReferenceException when serializing derived types whose base class defines the ShouldSerialize method. The MemberMapping already stores the correctly resolved MethodInfo (CheckShouldPersistMethodInfo), which is resolved without DeclaredOnly during import and used by the IL-gen path. Use it directly in the reflection path to match the IL-gen behavior. Fix #120561 --- .../ReflectionXmlSerializationWriter.cs | 8 ++----- .../tests/XmlSerializer/XmlSerializerTests.cs | 24 +++++++++++++++++++ .../tests/SerializationTypes.cs | 22 +++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index 679f74eb2d9fb8..fba464fcbf582c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -661,9 +661,7 @@ private void WriteStructMethod(StructMapping mapping, string n, string? ns, obje if (m.CheckShouldPersist) { - string methodInvoke = $"ShouldSerialize{m.Name}"; - MethodInfo method = o!.GetType().GetMethod(methodInvoke, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)!; - shouldPersist = (bool)method.Invoke(o, Array.Empty())!; + shouldPersist = (bool)m.CheckShouldPersistMethodInfo!.Invoke(o, Array.Empty())!; } if (m.Attribute != null) @@ -693,9 +691,7 @@ private void WriteStructMethod(StructMapping mapping, string n, string? ns, obje if (m.CheckShouldPersist) { - string methodInvoke = $"ShouldSerialize{m.Name}"; - MethodInfo method = o!.GetType().GetMethod(methodInvoke, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)!; - shouldPersist = (bool)method.Invoke(o, Array.Empty())!; + shouldPersist = (bool)m.CheckShouldPersistMethodInfo!.Invoke(o, Array.Empty())!; } bool checkShouldPersist = m.CheckShouldPersist && (m.Elements!.Length > 0 || m.Text != null); diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index a559c4fd5a8199..7acf58d4cfb7f3 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -1676,6 +1676,30 @@ public static void Xml_TypeWithShouldSerializeMethod_WithNonDefaultValue() Assert.Equal(value.Foo, actual.Foo); } + [Fact] + public static void Xml_InheritedShouldSerializeMethod_WithDefaultValue() + { + var value = new DerivedTypeWithInheritedShouldSerialize(); + + var actual = SerializeAndDeserialize(value, WithXmlHeader("")); + + Assert.NotNull(actual); + Assert.Equal(value.Foo, actual.Foo); + Assert.Equal(value.Bar, actual.Bar); + } + + [Fact] + public static void Xml_InheritedShouldSerializeMethod_WithNonDefaultValue() + { + var value = new DerivedTypeWithInheritedShouldSerialize() { Foo = "SomeValue", Bar = "SomeBar" }; + + var actual = SerializeAndDeserialize(value, WithXmlHeader("SomeValue")); + + Assert.NotNull(actual); + Assert.Equal(value.Foo, actual.Foo); + Assert.Equal(value.Bar, actual.Bar); + } + [Fact] public static void Xml_KnownTypesThroughConstructorWithArrayProperties() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index edc034aad2e01b..5f48360136378c 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -924,6 +924,28 @@ public bool ShouldSerializeFoo() } } + public class BaseTypeWithShouldSerializeMethod + { + public string Foo { get; set; } = "default"; + + [System.Xml.Serialization.XmlAttribute] + public string Bar { get; set; } = "default"; + + public bool ShouldSerializeFoo() + { + return Foo != "default"; + } + + public bool ShouldSerializeBar() + { + return Bar != "default"; + } + } + + public class DerivedTypeWithInheritedShouldSerialize : BaseTypeWithShouldSerializeMethod + { + } + public class KnownTypesThroughConstructorWithArrayProperties { public object StringArrayValue;