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?
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;
}
}
This is indeed frustrating behavior of XML deserialization when lists contain a set of default entries created in the constructor.
My workaround was to set the XMLIgnoreAttribute on the List and include a public member of array of the object type with the set/get handling the population of the list from the array.
Something like the following allows for creating defaults in the constructor but keeps the XML serializer from adding entries to the default list. (error/nul validations aside).
public class Config
{
public Config()
{
Test1 = new List<string>() { "A", "B" };
Test2 = new String[] { "A", "B" };
}
[XmlIgnore]
public List<string> Test1 { get; set; }
public string[] Test2 { get; set; }
// This member is only to be used during XML serialization
public string[] Test1_Array
{
get
{
return Test1.ToArray();
}
set
{
Test1 = value.ToList();
}
}
}
精彩评论