XmlSerializer и List <T> со значениями по умолчанию
Я наблюдал странное поведение при сериализации и десериализации класса, который имеет член типа List<T>
, который был заполнен значениями по умолчанию во время построения. В отличие от свойства массива свойство типа List<T>
не будет опустошено при десериализации с помощью XmlSerializer.
Вот мой код:
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));
}
}
}
И это результат:
<?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>
Почему List<T>
обрабатывается по-разному с помощью XmlSerializer, чем массив, и что я могу сделать, чтобы изменить это поведение?
Ответы
Ответ 1
Интересно; Я никогда не замечал этого в прошлом, но он определенно воспроизводимый. Поскольку XmlSerializer
не поддерживает обратные вызовы сериализации (чтобы вы знали, что он работает для сериализации), это трудно повлиять; возможно, самым простым ответом является "не помещать данные по умолчанию в объекты в конструкторе" (хотя, возможно, предлагает метод factory, который это делает).
Вы можете попробовать реализовать IXmlSerializable
, но это слишком сложно, даже для простого примера.
Я проверил, хотя, и DataContractSerializer
не ведет себя так, чтобы вы могли перейти на DataContractSerializer
; здесь мой тестовый код с 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));
}
}
и вот что я подразумеваю под методом factory:
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;
}
}
Ответ 2
Это действительно разочаровывает поведение десериализации XML, когда списки содержат набор записей по умолчанию, созданных в конструкторе.
Мое обходное решение состояло в том, чтобы установить XMLIgnoreAttribute в List и включить публичный член массива типа объекта, когда set/get обрабатывает совокупность списка из массива.
Что-то вроде следующего позволяет создавать значения по умолчанию в конструкторе, но не позволяет сериализатору XML добавлять записи в список по умолчанию. (ошибка/nul валидации в стороне).
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();
}
}
}