Skip to content

XmlSerializer does not respect member type specified in XmlElement attribute when DynamicCodeSupport is false #108432

Open

Description

Description

When there are multiple XmlElement attributes attached to a property, and one of them specifies that the member type is of some base type A, serializing an object whose property is initialized with type B, where B : A, throws with:

Unhandled exception. System.InvalidOperationException: There was an error generating the XML document.
 ---> System.InvalidOperationException: The type B was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

This was reported to us by a customer who experienced the problem when targeting iOS platforms with .NET as the DynamicCodeSupport feature switch is false by default on these platforms on all supported runtimes: NativeAOT and MonoAOT.

For reference, I created a console app reproduction which shows that this only happens when DynamicCodeSupport is set to false.

Repro

If we consider the following example:

using System.Xml.Serialization;

var container = new Container
{
    Items = new()
    {
        new B()
    }
};

XmlSerializer serializer = new XmlSerializer(typeof(Container));
using (StringWriter writer = new StringWriter())
{
    serializer.Serialize(writer, container);
    string xmlString = writer.ToString();
    Console.WriteLine(xmlString);
}

[XmlInclude(typeof(B))]
public class A { }
public class B : A { }
public class C { }

public class Container
{
    [XmlElement("As", typeof(A))]
    [XmlElement("Cs", typeof(C))]
    public List<object> Items { get; set; }
}

and build/run it as a console application with dotnet run , the output is as follows:

<?xml version="1.0" encoding="utf-16"?>
<Container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <As xsi:type="B" />
</Container>

When we rebuild and rerun the application with DynamicCodeSupport set to false ie: dotnet run -p:DynamicCodeSupport=false the app throws with:

Unhandled exception. System.InvalidOperationException: There was an error generating the XML document.
 ---> System.InvalidOperationException: The type B was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteElements(Object o, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, Boolean writeAccessors, Boolean isNullable)
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteArrayItems(ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, Object o)
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteStructMethod(StructMapping mapping, String n, String ns, Object o, Boolean isNullable, Boolean needType)
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteElements(Object o, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, Boolean writeAccessors, Boolean isNullable)
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.GenerateTypeElement(Object o, XmlTypeMapping xmlMapping)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at Program.<Main>$(String[] args) in /Users/ivan/tmp/net8/MultipleXmlElementsWithDerivedTypes/Program.cs:line 14

Additional notes

This is probably similar issue to #107252 where we use ReflectionOnly serialization.

FWIW, when we keep only one XmlElement attribute (relevant for the test case) attached to the property, serialization works fine even with DynamicCodeSupport=false.

public class Container
{
    [XmlElement("As", typeof(A))]
-    [XmlElement("Cs", typeof(C))]
    public List<object> Items { get; set; }
}

Known workarounds when targeting iOS platforms

Enable interpreter by adding the following in the project file:

<UseInterpreter>true</UseInterpreter>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions