Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlSerializer and List<T> with default values

I observed a weird behavior when serializing and than deserializing a class that is having a member of type List<T> which was filled with default values at construction time. Unlike the array based property the property of type List<T> won't get emptied at deserialization by the XmlSerializer.

Here is my code:

public class Program
{
    public class Config
    {
        public Config()
        {
            Test1 = new List<string>()  {"A", "B"};
            Test2 = new String[] {"A", "B"};
        }
        public List<string> Test1 {get;set;}
        public string[] Test2 {get;set;}
    }

    public static void Main()
    {
        XmlSerializer xmlSerializer =
            new XmlSerializer(typeof(Config));
        using(Stream s = new MemoryStream())
        {
            xmlSerializer.Serialize(s, new Config());
            s.Position = 0;
            xmlSerializer.Serialize(Console.Out,
                xmlSerializer.Deserialize(s));
        }
    }
}

And this is the output:

<?xml version="1.0" encoding="ibm850"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Test1>
    <string>A</string>
    <string>B</string>
    <string>A</string>
    <string>B</string>
  </Test1>
  <Test2>
    <string>A</string>
    <string>B</string>
  </Test2>
</Config>

Why is the List<T> handled differently by XmlSerializer than the array and what can I do to change this behavior?

like image 953
Martin Avatar asked Nov 18 '10 16:11

Martin


People also ask

How to put default value in XML?

You can specify the default value of an XML element or XML attribute by applying a DefaultValueAttribute to a member. To examine the result of applying the value, compile the application into a DLL or executable, and pass the resulting file as an argument to the XML Schema Definition tool (XSD.exe).

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.

Is XmlSerializer thread safe?

Since XmlSerializer is one of the few thread safe classes in the framework you really only need a single instance of each serializer even in a multithreaded application.

How does the XmlSerializer work C#?

The XmlSerializer creates C# (. cs) files and compiles them into . dll files in the directory named by the TEMP environment variable; serialization occurs with those DLLs. These serialization assemblies can be generated in advance and signed by using the SGen.exe tool.


1 Answers

Interesting; I've never noticed that in the past, but it is definitely reproducible. Since XmlSerializer doesn't support serialization callbacks (to help you know that it is running for serialization) this is hard to influence; arguably the simplest answer is "don't put default data into the objects in the constructor" (although maybe offer a factory method that does that).

You could try implementing IXmlSerializable, but that is overly hard to get right, even for a simple example.

I have checked, though, and DataContractSerializer does not behave this way - so you could perhaps switch to DataContractSerializer; here's my test code with DCS:

DataContractSerializer ser =
    new DataContractSerializer(typeof(Config));
using (Stream s = new MemoryStream())
{
    ser.WriteObject(s, new Config());
    s.Position = 0;
    using(var writer = XmlWriter.Create(Console.Out)) {
        ser.WriteObject(writer, ser.ReadObject(s));
    }
}

and here is what I mean by a the factory method:

public class Config
{
    public Config()
    {
        Test1 = new List<string>();
        Test2 = nix;
    }
    public List<string> Test1 { get; set; }
    public string[] Test2 { get; set; }

    private static readonly string[] nix = new string[0];
    public static Config CreateDefault()
    {
        Config config = new Config();
        config.Test1.Add("A");
        config.Test1.Add("B");
        config.Test2 = new string[2] { "A", "B" };
        return config;
    }
}
like image 112
Marc Gravell Avatar answered Sep 25 '22 15:09

Marc Gravell