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?
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).
Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.
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.
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With