集合属性的XML反序列化code默认 [英] XML Deserialization of collection property with code defaults

查看:238
本文介绍了集合属性的XML反序列化code默认的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有关应用程序的配置,我经常会创建一个配置值的配置类的应用程序,然后我反序列化到一个对象利用。配置对象通常是数据绑定到用户接口控制,使得配置可被改变,并且由用户持续。配置类通常具有分配给该属性,以便总有一个缺省配置的默认值。这一直运作良好。我最近有一个情况我有这样的提供了一些默认的路径信息字符串列表。而我所看到的让我意识到我并不完全知道如何对象属性的XML反序列化一个对象时被填充。

For application configuration, I frequently will create a configuration class with configuration values for the application that I then deserialize into an object to utilize. The configuration object is usually databound to a user interface control so that the configuration can be changed and persisted by the user. The configuration class typically has default values assigned to the properties so that there is always a default configuration. This has worked well. I recently had a situation where I had a list of strings that provided some default path information. And what I saw made me realize I did not completely know how the object properties are being populated during XML deserialization to an object.

所以,我创建了一个简单的例子来说明的行为。下面是一个简单的类,有几个有一些code默认属性。

So I created a simple example to show the behavior. The following is a simple class that has a couple of properties that have some code defaults.

[Serializable]
public class TestConfiguration
   {
      public String Name 
      { 
         get
         {
            return mName;
         }
         set
         {
            mName = value;
         }
      }private String mName = "Pete Sebeck";

  public List<String> Associates 
  { 
     get
     {
        return mAssociates;
     }
     set
     {
        mAssociates = value;
     }
  } private List<String> mAssociates = new List<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();
  }
   }

这里是一个主创建一个新对象,打印对象的状态到控制台,序列化(XML)到一个文件中,从该文件中重新构成一个对象并再次打印对象的状态到安慰。我的预期是,所匹配的部分连载的对象。我得到的是与序列化列表的内容的默认对象添加到默认的。

And here is a main that creates a new objects, prints the state of the object to the console, serializes (xml) it to a file, the reconstitutes an object from that file and again prints the state of the object to the console. What I expected was an object that matched what was serialized. What I got was the default object with contents of the serialized list added to the default.

  static void Main(string[] args)
  {
     // Create a default object
     TestConfiguration configuration = new TestConfiguration();
     Console.WriteLine(configuration.ToString());

     // Serialize the object
     XmlSerializer writer = new XmlSerializer(typeof(TestConfiguration));
     StreamWriter filewriter = new StreamWriter("TestConfiguration.xml");
     writer.Serialize(filewriter, configuration);
     filewriter.Close();

     // Now deserialize the xml into another object
     XmlSerializer reader = new XmlSerializer(typeof(TestConfiguration));
     StreamReader filereader = new StreamReader("TestConfiguration.xml");
     TestConfiguration deserializedconfiguration = (TestConfiguration)reader.Deserialize(filereader);
     filereader.Close();

     Console.WriteLine(deserializedconfiguration.ToString());

     Console.ReadLine();
      }

结果:

Name: Pete Sebeck
Associates:
        Jon
        Natalie

Name: Pete Sebeck
Associates:
        Jon
        Natalie
        Jon
        Natalie

我猜我一直以为列表会被设置,而不是追加。有没有人有一个指针为集合反序列化进程?我显然不知道现在正确的搜索条款我尝试都上来了空。我看到其他职位描述我所看到和实施系列化自己的做法。我比较期待的是描述一个集合进行反序列化会发生什么,所以我可以对自己解释我所看到的指针。

I guess I always thought the List property would be set rather than appended to. Does anyone have a pointer to the deserialization process for collections? I apparently do now know the correct search terms as my attempts are coming up empty. I see other posts describing what I am seeing and their approach of implementing serialization themselves. I am more looking for a pointer that describes what happens when a collection is deserialized so I can explain to myself what I am seeing.

推荐答案

您是正确的,许多串行器(虽然不是全部)以这种方式工作。 Json.NET呢,它的<一个href=\"http://james.newtonking.com/json/help/index.html?topic=html/M_Newtonsoft_Json_JsonConverter_ReadJson.htm\"相对=nofollow> JsonConverter.ReadJson 方法实际上有一个对象existingValue 正是这种情况。

You are correct that many serializers (though not all) work this way. Json.NET does, its JsonConverter.ReadJson method actually has an Object existingValue for exactly this situation.

我不知道这些各种各样的实现细节都有明确规定的任何文件。以确定是否串行最简单的方法使用pre分配集合时present,而不是无条件地分配,然后设置其中一个本身就是实际使用的ObservableCollection&LT测试; T&GT; ,当它改变时安装调试监听器:

I don't know of any documents where these sorts of implementation details are spelled out. The easiest way to determine whether a serializer uses pre-allocated collections when present rather than unconditionally allocating and then setting one itself is to actually test it by using an ObservableCollection<T> and attaching debug listeners when it is changed:

[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; // In case it was already there.
            list.CollectionChanged += list_CollectionChanged;
        }
        return list;
    }

    static ObservableCollection<String> RemoveListeners(ObservableCollection<String> list)
    {
        if (list != null)
        {
            list.CollectionChanged -= list_CollectionChanged; // In case it was already there.
        }
        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());
        }
    }
}

我跑上面测试,结果发现:

I ran the test above, and found:


  1. 的XmlSerializer 和Json.NET使用pre-现有的集合,如果present。 (在Json.NET这可以通过设置<一个来控制href=\"http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_ObjectCreationHandling.htm\"相对=nofollow> JsonSerializerSettings.ObjectCreationHandling 来的 替换

  2. 的JavaScriptSerializer 的BinaryFormatter 的DataContractSerializer 不,始终分配收集自己。对于后两者这并不奇怪,因为这两种<一个href=\"https://stackoverflow.com/questions/1076730/datacontractserializer-doesnt-call-my-constructor\">do不叫默认构造的,而是干脆直接分配空的内存。

  1. XmlSerializer and Json.NET use the pre-existing collection if present. (In Json.NET this can be controlled by setting JsonSerializerSettings.ObjectCreationHandling to Replace)
  2. JavaScriptSerializer, BinaryFormatter and DataContractSerializer do not, and always allocate the collection themselves. For the latter two this is not surprising as both do not call default constructors and instead simply allocate empty memory directly.

我不知道为什么,案例1中的序列化行为这种方式。也许他们的作者关心的是,包含类可能要在内部使用的子类的被反序列化收集,或附加观察员观察到的藏品,因为我已经做了,所以决定遵守这一设计?

I don't know why the serializers in case 1 behave this way. Perhaps their authors were concerned that the containing class might want to internally use a subclass of the collection being deserialized, or attach observers to observable collections as I have done, and so decided to honor that design?

一注 - 为所有串行(除了,也许,的BinaryFormatter ,对此我不确定),如果集合属性的特别声明为数组的那么串行器将分配数组本身并设置阵列后,它已完全填充。这意味着<一href=\"https://stackoverflow.com/questions/26179792/cannot-deserialize-xml-into-a-list-using-xml-deserializer/26624551#26624551\">arrays序列化过程中总是可以用作代理服务器的集合。

One note - for all serializers (except, maybe, BinaryFormatter, about which I am unsure), if a collection property is declared specifically as an array then the serializer will allocate the array itself and set the array after it is fully populated. This means that arrays can always be used as proxy collections during serialization.

通过使用代理服务器阵列,可以保证反序列化期间您的收藏被覆盖:

By using a proxy array, you can guarantee that your collection is overwritten during deserialization:

    [IgnoreDataMember]
    [XmlIgnore]
    [ScriptIgnore]
    public ObservableCollection<String> { get; set; } // Or List<string> or etc.

    [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个串行只有previously系列化成员。

Now the collection comes back with only the previously serialized members with all 5 serializers.

这篇关于集合属性的XML反序列化code默认的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆