5

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?

SimpleNotGood
  • 345
  • 3
  • 14

2 Answers2

8

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:

    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:

Working .Net fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
1

Given the XML shown in the initial version of your question:

<List>
    <Element Name="A"/>
    <Element Name="B"/>
    <Element Name="C"/>
</List> 

And assuming your Element type looks like:

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

Then this type is so simple that you can construct it manually like so:

var list = doc
    // Get the first elements named List
    .Descendants("List").Take(1)
    // Get all children of List named Element
    .SelectMany(l => l.Elements("Element"))
    // Get the attribute of Element named Name, cast its value to a string, 
    // and create a c# Element from it using the specified name.
    .Select(e => new Element { Name = (string)e.Attribute("Name") } )
    // Materialize as a List<Element>
    .ToList();

Here I am using an explicit casting operator of XAttribute to convert the Name attribute to a string value. There are also explicit casting operators for XElement to convert an element value to a primitive such as a string, an int or a nullable DateTime among others. These operators can be used when populating a c# type as shown above.

(Switching to DataContractSerializer would not be easier since this serializer does not support XML attributes out of the box meaning you would have to do something manual anyway.)

Sample working .Net fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thank you very much. Is there a way to create the c# Element without having to asign every XML attribute to an object attribute in code like it is done in this line: `.Select(e => new Element { Name = (string)e.Attribute("Name") } )`? But rather do it automatically like the Deserialize function of the XmlSerializer does? – SimpleNotGood Dec 28 '17 at 08:32