Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative for deserializing XML with XmlSerializer

I am programming an UWP app and want to deserialize a list inside a xml to a list of Objects:

<List>
    <Element Name="A">
        <SubElement Name="A1"> 
            <Color> Blue </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="A2"> 
            <Color> Blue </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
    <Element Name="B">
        <SubElement Name="B1"> 
            <Color> Yellow </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="B2"> 
            <Color> Yellow </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
    <Element Name="C"/>
        <SubElement Name="C1"> 
            <Color> Purple </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="C2"> 
            <Color> Purple </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
</List> 

The classes look like this:

public class Element
{
    [XmlAttribute]
    public string Name { get; set; }

    [XmlElement("SubElement")]
    public List<SubElement> _subelement { get; set; }
}

public class SubElement
{
      [XmlElement("Color")]
      public string Color{ get; set; }

      [XmlAttribute("Name")]
      public string Name { get; set; } 
}

My C# code looks like this:

XDocument doc = XDocument.Load("list.xml");
XElement node = doc.Descendants(XName.Get("List")).FirstOrDefault();
var serializer = new XmlSerializer(typeof(List<Element>), new XmlRootAttribute("List"));
var elementList = serializer.Deserialize(node.CreateReader()) as List<Element>;

In debug mode this application works fine. In release mode I get a PlatformNotSupportedException error as in this thread. I found out that the problem must have something to do with the Compile with .NET Native tool chain Option in the project settings. But I found no solution for the problem.

My Question: Is there a simple alternative for the code shown above to deserialize XML to Objects, which doesn't use XmlSerializer and works in an UWP app?

like image 830
SimpleNotGood Avatar asked Jan 29 '23 23:01

SimpleNotGood


1 Answers

In your rewritten question, your XML would seem to have arbitrary complexity with a mixture of elements and attributes. You are seeking a way to deserialize this XML automatically without use of XmlSerializer, which basically amounts to writing another XML serializer.

Since this is nontrivial (and outside the scope of a stackoverflow answer), one quick workaround would be to translate the XML to a format recognized by some other serializer, and use that. Possibilities include:

  1. DataContractSerializer. This serializer is intended for XML deserialization, but does not support:

    • XML attribute deserialization.
    • Deserialization of sequences (such as <Element /><Element/>) as collections without an outer element.

    So, while it would be possible to deserialize your XML to some appropriate data model with this serializer, substantial preprocessing using LINQ to XML would be required.

  2. Json.NET. This serializer supports conversion of XML to JSON as documented in Converting between JSON and XML and thus may be appropriate to your needs.

If you choose option #2, you could define your data model as follows:

public class RootObject
{
    [JsonConverter(typeof(SingleOrArrayConverter<Element>))]
    public List<Element> Element { get; set; }
}

public class Element
{
    [XmlAttribute]
    [JsonProperty("@Name")]
    public string Name { get; set; }

    [XmlElement("SubElement")]
    [JsonProperty("SubElement")]
    [JsonConverter(typeof(SingleOrArrayConverter<SubElement>))]
    public List<SubElement> _subelement { get; set; }
}

public class SubElement
{
    [XmlElement("Color")]
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string> Color { get; set; }

    [XmlAttribute("Name")]
    [JsonProperty("@Name")]
    public string Name { get; set; }
}

And deserialize as follows:

var doc = XDocument.Parse(xmlString);

// Convert the XDocument to an intermediate JToken hierarchy.
var converter = new Newtonsoft.Json.Converters.XmlNodeConverter { OmitRootObject = true };
var rootToken = JObject.FromObject(doc, JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = { converter } }));

// Convert the JTOken to a RootObject
var rootObj = rootToken.ToObject<RootObject>();

Notes:

  • In your question your SubElement has a single Color property, but in your XML there are clearly multiple <Color> elements for each <SubElement>. Thus I had to change it to public List<string> Color { get; set; }.

  • SingleOrArrayConverter<> is taken verbatim from this answer to How to handle both a single item and an array for the same property using JSON.net by Brian Rogers. It is needed to handle the case where a single child node is converted to a JSON object rather than a JSON array.

Working .Net fiddle.

like image 134
dbc Avatar answered Jan 31 '23 12:01

dbc