您是正确的,许多序列化器(但不是全部)都是这样工作的。Json.NET就是这样做的,它的
JsonConverter.ReadJson
方法实际上有一个
Object existingValue
来处理这种情况。
我不知道是否有任何文件详细说明这些实现细节。确定序列化器是否在存在时使用预分配的集合而不是无条件地分配并自行设置一个集合的最简单方法是通过使用
ObservableCollection<T>
进行测试,并在更改时附加调试侦听器。
[Serializable]
[DataContract]
public class TestConfiguration
{
[DataMember]
public String Name { get { return mName; } set { mName = value; } }
private String mName = "Pete Sebeck";
[DataMember]
public ObservableCollection<String> Associates
{
get
{
Debug.WriteLine(mAssociates == null ? "Associates gotten, null value" : "Associates gotten, count = " + mAssociates.Count.ToString());
return mAssociates;
}
set
{
Debug.WriteLine(value == null ? "Associates set to a null value" : "Associates set, count = " + value.Count.ToString());
RemoveListeners(mAssociates);
mAssociates = AddListeners(value);
}
}
private ObservableCollection<String> mAssociates = AddListeners(new ObservableCollection<string>() { "Jon", "Natalie" });
public override String ToString()
{
StringBuilder buffer = new StringBuilder();
buffer.AppendLine(String.Format("Name: {0}", Name));
buffer.AppendLine("Associates:");
foreach (String associate in mAssociates)
{
buffer.AppendLine(String.Format("\t{0}", associate));
}
return buffer.ToString();
}
static ObservableCollection<String> AddListeners(ObservableCollection<String> list)
{
if (list != null)
{
list.CollectionChanged -= list_CollectionChanged;
list.CollectionChanged += list_CollectionChanged;
}
return list;
}
static ObservableCollection<String> RemoveListeners(ObservableCollection<String> list)
{
if (list != null)
{
list.CollectionChanged -= list_CollectionChanged;
}
return list;
}
public static ValueWrapper<bool> ShowDebugInformation = new ValueWrapper<bool>(false);
static void list_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (!ShowDebugInformation)
return;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Debug.WriteLine(string.Format("Added {0} items", e.NewItems.Count));
break;
case NotifyCollectionChangedAction.Move:
Debug.WriteLine("Moved items");
break;
case NotifyCollectionChangedAction.Remove:
Debug.WriteLine(string.Format("Removed {0} items", e.OldItems.Count));
break;
case NotifyCollectionChangedAction.Replace:
Debug.WriteLine("Replaced items");
break;
case NotifyCollectionChangedAction.Reset:
Debug.WriteLine("Reset collection");
break;
}
}
}
public static class TestTestConfiguration
{
public static void Test()
{
var test = new TestConfiguration();
Debug.WriteLine("\nTesting Xmlserializer...");
var xml = XmlSerializationHelper.GetXml(test);
using (new SetValue<bool>(TestConfiguration.ShowDebugInformation, true))
{
var testFromXml = XmlSerializationHelper.LoadFromXML<TestConfiguration>(xml);
Debug.WriteLine("XmlSerializer result: " + testFromXml.ToString());
}
Debug.WriteLine("\nTesting Json.NET...");
var json = JsonConvert.SerializeObject(test, Formatting.Indented);
using (new SetValue<bool>(TestConfiguration.ShowDebugInformation, true))
{
var testFromJson = JsonConvert.DeserializeObject<TestConfiguration>(json);
Debug.WriteLine("Json.NET result: " + testFromJson.ToString());
}
Debug.WriteLine("\nTesting DataContractSerializer...");
var contractXml = DataContractSerializerHelper.GetXml(test);
using (new SetValue<bool>(TestConfiguration.ShowDebugInformation, true))
{
var testFromContractXml = DataContractSerializerHelper.LoadFromXML<TestConfiguration>(contractXml);
Debug.WriteLine("DataContractSerializer result: " + testFromContractXml.ToString());
}
Debug.WriteLine("\nTesting BinaryFormatter...");
var binary = BinaryFormatterHelper.ToBase64String(test);
using (new SetValue<bool>(TestConfiguration.ShowDebugInformation, true))
{
var testFromBinary = BinaryFormatterHelper.FromBase64String<TestConfiguration>(binary);
Debug.WriteLine("BinaryFormatter result: " + testFromBinary.ToString());
}
Debug.WriteLine("\nTesting JavaScriptSerializer...");
var javaScript = new JavaScriptSerializer().Serialize(test);
using (new SetValue<bool>(TestConfiguration.ShowDebugInformation, true))
{
var testFromJavaScript = new JavaScriptSerializer().Deserialize<TestConfiguration>(javaScript);
Debug.WriteLine("JavaScriptSerializer result: " + testFromJavaScript.ToString());
}
}
}
我运行了上面的测试,并发现:
XmlSerializer
和 Json.NET 会使用已存在的集合。 (在 Json.NET 中,可以通过将 JsonSerializerSettings.ObjectCreationHandling
设置为 Replace
来控制这一点)
JavaScriptSerializer
、BinaryFormatter
和 DataContractSerializer
不会使用已存在的集合,而总是自己分配集合。对于后两者来说,这并不奇怪,因为它们都不调用默认构造函数,而是直接分配空内存。
我不知道为什么情况1中的序列化程序会以这种方式行事。也许它们的作者担心包含类可能希望在反序列化时使用集合的子类,或者像我一样向可观察集合附加观察器,因此决定遵守该设计?
注意:对于所有序列化程序(除了可能是
BinaryFormatter
,我不确定),如果一个集合属性被明确声明为数组,则序列化程序将分配数组并在完全填充后设置数组。这意味着
数组始终可以在序列化期间用作代理集合。
通过使用代理数组,您可以保证在反序列化期间覆盖您的集合:
[IgnoreDataMember]
[XmlIgnore]
[ScriptIgnore]
public ObservableCollection<String> { get; set; }
[XmlArray("Associates")]
[DataMember(Name="Associates")]
public string[] AssociateArray
{
get
{
return (Associates == null ? null : Associates.ToArray());
}
set
{
if (Associates == null)
Associates = new ObservableCollection<string>();
Associates.Clear();
if (value != null)
foreach (var item in value)
Associates.Add(item);
}
}
现在集合只返回所有5个序列化器中先前序列化的成员。