I would like to deserialize an XML File to a class with several subclasses. The XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Objects>
<Group index="1">
<de>
<GroupName>ANTRIEB</GroupName>
</de>
<en>
<GroupName>missing translation!</GroupName>
</en>
<Level>2</Level>
</Group>
<Group index="2">
<de>
<GroupName>BREMSEN</GroupName>
</de>
<Level>3</Level>
</Group>
</Objects>
Deserializing the XML to classes would be no problem, if there wouldn't be those language tags. Sure, I could create a property for every language tag possible. But the list of languages possible should be dynamic (e.g. read from an config file).
This is the reason why i would like to deserialize those language tags and their content into a Dictionary which uses the language as key and a model for the content.
My models look like this:
[XmlRoot("Objects")]
public class DeactivationsXml
{
[XmlElement("Group")]
public DeactivationsGroup[] Groups { get; set; }
}
[Serializable()]
public class DeactivationsGroup
{
[XmlIgnore]
public Dictionary<string, GroupName> GroupNames { get; set; } = new Dictionary<string, GroupName>();
public int Level { get; set; }
[XmlAttribute]
public byte index { get; set; }
}
public class GroupName
{
[XmlElement("GroupName")]
public string Name { get; set; }
}
I searched for a long time to address this problem, but couldn't find a solution. I'm pretty sure, that it's not possible to solve this Problem just with attributes.
Does some hybrid aproach exist in order to combine the Deserialization of an XML File in combination with manual deserialization of all XmlElements which could not be automatically deserialized?
A good and extensible solution for my problem would be great, because the XML structure is complex (same Problem several times with different content etc.). I can't change the structure of the XML, so please don't point this out.
Approaches
IXmlSerializable
I tried to implement the IXmlSerializable Interface on the DeactivationsGroup class in order to search with a list of given languages for XmlElements with those names and deserialize the content of those XmlElements.
But this approach didn't work out, because you have to map all properties manually.
IExtensibleDataObject
The Interface is only supported by a DataContractSerializer. In the worst case i could use this interface to deserialize after Deserializing, if no other solution is found..
OnDeserialization
This Attribute is not supported by XmlSerializer, but would provide the functionality i possibly need.
XmlAnyElement
I guess this is the best option at this point. Does some callback exist after deserialization finished in order to automate this?
Executable Code
Here's the whole code so far.
public void Parse()
{
string xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
" <Objects>" +
" <Group index=\"1\">" +
" <de>" +
" <GroupName>ANTRIEB</GroupName>" +
" </de>" +
" <en>" +
" <GroupName>missing translation!</GroupName>" +
" </en>" +
" <Level>2</Level>" +
" </Group>" +
" <Group index=\"2\">" +
" <de>" +
" <GroupName>BREMSEN</GroupName>" +
" </de>" +
" <Level>3</Level>" +
" </Group>" +
" </Objects>";
XmlSerializer serializer = new XmlSerializer(typeof(DeactivationsXml));
using (TextReader fileStream = new StringReader(xml))
{
var result = (DeactivationsXml)serializer.Deserialize(fileStream);
}
}
[XmlRoot("Objects")]
public class DeactivationsXml
{
[XmlElement("Group")]
public DeactivationsGroup[] Groups { get; set; }
}
[Serializable()]
public class DeactivationsGroup
{
[XmlIgnore]
public Dictionary<string, GroupName> GroupNames { get; set; } = new Dictionary<string, GroupName>();
public int Level { get; set; }
[XmlAttribute]
public byte index { get; set; }
}
public class GroupName
{
[XmlElement("GroupName")]
public string Name { get; set; }
}