代码之家  ›  专栏  ›  技术社区  ›  Martin

使用默认值的xmlserializer和list<t>

  •  12
  • Martin  · 技术社区  · 14 年前

    我在序列化和反序列化具有类型成员的类时观察到一种奇怪的行为。 List<T> 它在构建时用默认值填充。与基于数组的属性不同,类型的属性 列表& T; XML序列化程序在反序列化时不会清空。

    这是我的代码:

    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>
    

    为什么是 列表& T; xmlserializer的处理方式与数组的处理方式不同,如何更改此行为?

    2 回复  |  直到 9 年前
        1
  •  5
  •   Marc Gravell    14 年前

    有趣的是,我从来没有注意到这一点,但它绝对是可复制的。自从 XmlSerializer 不支持序列化回调(帮助您了解它正在运行以进行序列化),这很难受到影响;可以说,最简单的答案是“不要将默认数据放入构造函数中的对象”(尽管可能提供一个这样做的工厂方法)。

    能够 尝试实施 IXmlSerializable 但即使是举个简单的例子,也很难做到正确。

    不过,我已经查过了,而且 DataContractSerializer 这样做-这样你就可以换到 DataContractSerializer ;这是我对分布式控制系统的测试代码:

    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));
        }
    }
    

    这就是我所说的工厂方法:

    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
  •  3
  •   Jezzamon    9 年前

    当列表包含在构造函数中创建的一组默认项时,这确实是令人沮丧的XML反序列化行为。

    我的解决方法是在列表上设置xmlignoreattribute,并使用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();
            }
        }        
    
    }