I would like to deserialize xml where the root class is an Abstract class but the root node in the xml has a value of the XML root attribute in the concrete class. The key is to do this without specifying the concrete class to deserialize in the XmlSerializer
constructor.
Example:
MessageType.cs
[Serializable]
[XmlInclude(typeof(Entity))]
public abstract class MessageType
{
}
Entity.cs
[Serializable]
[XmlRoot("Entity", IsNullable = false)]
public class Entity : MessageType
{
private string data;
public string Data
{
get { return data; }
set { data = value; }
}
}
Program.cs
var entity = new Entity
{
Data = "test"
};
var xs = new XmlSerializer(typeof(Entity));
using var stream = new System.IO.StringWriter();
xs.Serialize(stream, entity);
var xml = stream.ToString();
using var stringReader = new System.IO.StringReader(xml);
xs = new XmlSerializer(typeof(MessageType));
var obj = serializer.Deserialize(stringReader);
This code results in
Unhandled exception. System.InvalidOperationException: There is an error in XML document (2, 2).
---> System.InvalidOperationException: <Entity xmlns=''> was not expected.
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMessageType.Read4_MessageType()
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
The xml produced by the serialization in the code above looks like:
<?xml version="1.0" encoding="utf-16"?>
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Data>test</Data>
</Entity>
Couple Extra Notes:
MessageType
class and
with xml deserialization seeming to be a slow process, looping over each possible type
and seeing if it can deserialize is not ideal<?xml version="1.0" encoding="utf-16"?>
<MessageType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="Entity">
<Data>test</Data>
</MessageType>
that it would work, but refer to note #1.You can get the name of the root node with the XmlReader. Then you can get the type by name and create the serializer accordingly.
using var xmlReader = XmlReader.Create(stream);
xmlReader.MoveToContent();
var rootNodeName = xmlReader.Name;
var implementationType = Type.GetType(rootNodeName);
if (implementationType != null)
{
var serializer = new XmlSerializer(implementationType);
var obj = serializer.Deserialize(xmlReader);
}
else
{
// unknown type
}