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>