Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlRootAttribute placement when deserializing to List

I have the following XML

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Sites>
  <Site>
    <Code>TWTR</Code>
    <Name>twitter.com</Name>
  </Site>
  <Site>
    <Code>FB</Code>
    <Name>facebook.com</Name>
  </Site>
  <Site>
    <Code>SO</Code>
    <Name>stackoverflow.com</Name>
  </Site>
</Sites>

This is the code:

public class Program
{
    static void Main(string[] args)
    {
        var fs = new FileStream(@"D:\temp\Sites.xml", FileMode.Open);
        var serializer = new XmlSerializer(typeof(List<Site>));
        var instance = (List<Site>)serializer.Deserialize(fs);
    }
}

[XmlRoot("Sites")]
public class Site
{
    public string Code { get; set; }
    public string Name { get; set; }
}

The exception I get is: <Sites xmlns=''> was not expected.. The reason for this error is usually, when I don't define an XmlRoot for the XmlSerializer. But as you can see, I did that by decorating the class Site with the XmlRootAttribute

To complete my confusion, the following trick works:

Replace

var serializer = new XmlSerializer(typeof(List<Site>));

with

var serializer = new XmlSerializer(typeof(List<Site>), new XmlRootAttribute("Sites"));

Am I missing something?

like image 317
Sandro Avatar asked Jan 13 '23 21:01

Sandro


2 Answers

If you have control over the XML, then simply change:

<Sites> 

To

<ArrayOfSite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

If you don't have control over the XML, create your own collection and deserialize into that.

[XmlRoot("Sites")]
public class Sites : List<Site>
{

}

Be careful when using the following constructor:

var serializer = new XmlSerializer(typeof(List<Site>), new XmlRootAttribute("Sites"));

As Microsoft points out here, if you don't cache the instance of the serializer associated with List<Site>, you will end up with leaky memory...

Dynamically Generated Assemblies

To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:

XmlSerializer.XmlSerializer(Type)

XmlSerializer.XmlSerializer(Type, String)

If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.

like image 181
Chris Gessler Avatar answered Jan 29 '23 02:01

Chris Gessler


The XmlRoot attribute you added to Site is not used, because you aren't deserializing an object of type Site. You are deserializing an object of type List<Site> and that's where the serializer looks for the XmlRoot attribute.

Your workaround actually is the correct solution. However, if you are performing this deserialization often in your program, be sure to cache the XmlSerializer instance, because that specific constructor doesn't internally cache the dynamically generated assemblies - this contrasts with the constructor you normally use.

like image 43
Daniel Hilgarth Avatar answered Jan 29 '23 01:01

Daniel Hilgarth