在C#中,如何将旧对象中的XML反序列化到更新对象中,并忽略缺少的XML元素?

在C#中,如何将旧对象中的XML反序列化到更新对象中,并忽略缺少的XML元素?,c#,xml-serialization,defaults,C#,Xml Serialization,Defaults,我拥有的是一个自定义设置文件,我使用XmlSerializer对其进行序列化/反序列化。我的对象定义中没有模式定义,也没有序列化标记,只有直接的对象序列化(尽管如果需要,我会添加它们) 我的问题是我需要向对象添加数据成员。如果我这样做,我知道旧的设置文件将不会反序列化 是否有一种方法可以为添加的成员指定默认值,或者有一种简单的方法可以在XML中缺少这些成员时忽略它们?它应该反序列化很好,它将只使用默认构造函数初始化项。因此它们将保持不变。来自 最佳实践为确保正确的版本控制行为,在不同版本之间修改

我拥有的是一个自定义设置文件,我使用
XmlSerializer
对其进行序列化/反序列化。我的对象定义中没有模式定义,也没有序列化标记,只有直接的对象序列化(尽管如果需要,我会添加它们)

我的问题是我需要向对象添加数据成员。如果我这样做,我知道旧的设置文件将不会反序列化


是否有一种方法可以为添加的成员指定默认值,或者有一种简单的方法可以在XML中缺少这些成员时忽略它们?

它应该反序列化很好,它将只使用默认构造函数初始化项。因此它们将保持不变。

来自

最佳实践为确保正确的版本控制行为,在不同版本之间修改类型时,请遵循以下规则:

  • 添加新的序列化字段时,应用OptionalFieldAttribute 属性

  • 从字段中删除NonSerializedAttribute属性时 在以前的版本中不可序列化),请应用 OptionalFieldAttribute属性

  • 对于所有可选字段,使用 可接受序列化回调,除非默认值为0或null

我试图模拟您的情况,在新版本的类中,有一个名为Element2的新成员。将我的新成员初始化为“这是新成员”,这是完整的证据

Test1假设您使用根类的旧定义仅使用一个Element1进行序列化

Test2使用根类的新定义进行序列化和反序列化时

要回答您的问题,无论以何种方式提供默认值,您都应该使用“OptionalField”

使用系统;
使用System.Runtime.Serialization;
使用System.IO;
公开课考试
{
[可序列化]
公共类根
{
[可选字段(VersionAdded=2)]//由Microsoft推荐
私有字符串mElement2=“这是新成员”;
公共字符串元素1{get;set;}
公共字符串Element2{get{return mElement2;}set{mElement2=value;}}
}
公共静态void Main(字符串[]s)
{
WriteLine(“使用根的旧定义序列化的测试”);
控制台。写线(“”);
当使用一个元素()序列化原始对象时测试;
控制台。写线(“”);
WriteLine(“使用根的新定义序列化的测试”);
控制台。写线(“”);
当使用两个元素()序列化原始对象时测试;
Console.ReadLine();
}
私有静态void TestReadingObjects(字符串xml)
{
System.Xml.Serialization.XmlSerializer XmlSerializer=
新的System.Xml.Serialization.XmlSerializer(typeof(Root));
System.IO.Stream=新内存流();
System.Text.asciencoding encoding=新的System.Text.asciencoding();
Byte[]bytes=encoding.GetBytes(xml);
stream.Write(字节,0,字节.长度);
流位置=0;
根r=(根)xmlSerializer.Deserialize(流);
WriteLine(string.Format(“元素1={0}”,r.Element1));
WriteLine(string.Format(“Element 2={0}”,r.Element2==null?“null”:r.Element2));
}
私有静态无效测试\u当\u原始\u对象\u被\u序列化\u与\u一个\u元素()时
{
测试读取对象(@“1”);
}
私有静态无效测试\u当\u原始\u对象\u与\u Two\u元素()序列化\u时
{
测试读取对象(@“12”);
}
}

//以下是输出

如果您遵循此模式,它将非常简单:

  • 通过实现ISerializable自己处理序列化/反序列化
  • 使用此选项可以序列化对象的成员和序列化版本号
  • 在反序列化代码上,针对版本号运行switch case语句。开始时,只有一个版本—初始反序列化代码。继续操作时,您将在新序列化的快照中标记一个较新的版本号
  • 对于对象的未来版本,始终保持现有反序列化代码不变,或者修改它以映射到重命名/重构的成员,主要是为新的序列化版本添加新的case语句

这样,,即使序列化快照是从以前版本的程序集生成的,您也能够成功地反序列化以前的数据。

您需要使用自定义方法手动处理它,并在序列化/OnSerialized/OnSerialized/OnSerialized/OnSerialized上使用适当的属性标记它们,然后手动确定如何初始化值(如果可以的话)

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx http://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializingattribute.aspx

更好的方法是先确定版本,然后使用策略模式进行反序列化。这并不总是可能的,所以在这种情况下使用我的建议

更新:前面的答案仅适用于二进制序列化。对于常规Xml,可以使用此方法

class Program
    {
        static void Main(string[] args)
        {
            Deserialize(@"..\..\v1.xml");
        }

        private static Model Deserialize(string file)
        {
            XDocument xdoc = XDocument.Load(file);
            var verAtt = xdoc.Root.Attribute(XName.Get("Version"));
            Model m = Deserialize<Model>(xdoc);

            IModelLoader loader = null;

            if (verAtt == null)
            {
                loader = GetLoader("1.0");
            }
            else
            {
                loader = GetLoader(verAtt.Value);
            }

            if (loader != null)
            {
                loader.Populate(ref m);
            }
            return m;
        }

        private static IModelLoader GetLoader(string version)
        {
            IModelLoader loader = null;
            switch (version)
            {
                case "1.0":
                    {
                        loader = new ModelLoaderV1();
                        break;
                    }
                case "2.0":
                    {
                        loader = new ModelLoaderV2();
                        break;
                    }
                case "3.0": { break; } //Current
                default: { throw new InvalidOperationException("Unhandled version"); }
            }
            return loader;
        }

        private static Model Deserialize<T>(XDocument doc) where T : Model
        {
            Model m = null;
            using (XmlReader xr = doc.CreateReader())
            {
               XmlSerializer xs = new XmlSerializer(typeof(T));
               m = (Model)xs.Deserialize(xr);
               xr.Close();
            }
            return m;
        }
    }

    public interface IModelLoader
    {
        void Populate(ref Model model);
    }

    public class ModelLoaderV1 : IModelLoader
    {
        public void Populate(ref Model model)
        {
            model.City = string.Empty;
            model.Phone = "(000)-000-0000";
        }
    }

    public class ModelLoaderV2 : IModelLoader
    {
        public void Populate(ref Model model)
        {
            model.Phone = "(000)-000-0000";
        }
    }

    public class Model
    {
        [XmlAttribute(AttributeName = "Version")]
        public string Version { get { return "3.0"; } set { } }
        public string Name { get; set; } //V1, V2, V3
        public string City { get; set; } //V2, V3
        public string Phone { get; set; } //V3 only

    }

NET为序列化/反序列化和版本控制提供了很多功能

1) 用户DataContract/DataMember属性和DataContractSerializer

2) 根据MSDN,这些变化正在打破

  • 更改数据协定的名称或命名空间值
  • 使用DataMemberAttribute的order属性更改数据成员的顺序
  • 重命名数据成员
  • 更改数据成员的数据协定
3) 当带有额外字段的类型反序列化为缺少字段的类型时,将忽略额外信息

4) 将缺少字段的类型反序列化为具有e的类型时
class Program
    {
        static void Main(string[] args)
        {
            Deserialize(@"..\..\v1.xml");
        }

        private static Model Deserialize(string file)
        {
            XDocument xdoc = XDocument.Load(file);
            var verAtt = xdoc.Root.Attribute(XName.Get("Version"));
            Model m = Deserialize<Model>(xdoc);

            IModelLoader loader = null;

            if (verAtt == null)
            {
                loader = GetLoader("1.0");
            }
            else
            {
                loader = GetLoader(verAtt.Value);
            }

            if (loader != null)
            {
                loader.Populate(ref m);
            }
            return m;
        }

        private static IModelLoader GetLoader(string version)
        {
            IModelLoader loader = null;
            switch (version)
            {
                case "1.0":
                    {
                        loader = new ModelLoaderV1();
                        break;
                    }
                case "2.0":
                    {
                        loader = new ModelLoaderV2();
                        break;
                    }
                case "3.0": { break; } //Current
                default: { throw new InvalidOperationException("Unhandled version"); }
            }
            return loader;
        }

        private static Model Deserialize<T>(XDocument doc) where T : Model
        {
            Model m = null;
            using (XmlReader xr = doc.CreateReader())
            {
               XmlSerializer xs = new XmlSerializer(typeof(T));
               m = (Model)xs.Deserialize(xr);
               xr.Close();
            }
            return m;
        }
    }

    public interface IModelLoader
    {
        void Populate(ref Model model);
    }

    public class ModelLoaderV1 : IModelLoader
    {
        public void Populate(ref Model model)
        {
            model.City = string.Empty;
            model.Phone = "(000)-000-0000";
        }
    }

    public class ModelLoaderV2 : IModelLoader
    {
        public void Populate(ref Model model)
        {
            model.Phone = "(000)-000-0000";
        }
    }

    public class Model
    {
        [XmlAttribute(AttributeName = "Version")]
        public string Version { get { return "3.0"; } set { } }
        public string Name { get; set; } //V1, V2, V3
        public string City { get; set; } //V2, V3
        public string Phone { get; set; } //V3 only

    }
 private static void Serialize(Model model)
        {
            XmlSerializer xs = new XmlSerializer(typeof(Model));
            FileStream f = File.Create(@"..\..\v1.xml");
            xs.Serialize(f, model);

            f.Close();
        }
private bool myVal=false;

[DefaultValue(false)]
 public bool MyProperty {
    get {
       return myVal;
    }
    set {
       myVal=value;
    }
 }