在C#中,如何将旧对象中的XML反序列化到更新对象中,并忽略缺少的XML元素?
我拥有的是一个自定义设置文件,我使用在C#中,如何将旧对象中的XML反序列化到更新对象中,并忽略缺少的XML元素?,c#,xml-serialization,defaults,C#,Xml Serialization,Defaults,我拥有的是一个自定义设置文件,我使用XmlSerializer对其进行序列化/反序列化。我的对象定义中没有模式定义,也没有序列化标记,只有直接的对象序列化(尽管如果需要,我会添加它们) 我的问题是我需要向对象添加数据成员。如果我这样做,我知道旧的设置文件将不会反序列化 是否有一种方法可以为添加的成员指定默认值,或者有一种简单的方法可以在XML中缺少这些成员时忽略它们?它应该反序列化很好,它将只使用默认构造函数初始化项。因此它们将保持不变。来自 最佳实践为确保正确的版本控制行为,在不同版本之间修改
XmlSerializer
对其进行序列化/反序列化。我的对象定义中没有模式定义,也没有序列化标记,只有直接的对象序列化(尽管如果需要,我会添加它们)
我的问题是我需要向对象添加数据成员。如果我这样做,我知道旧的设置文件将不会反序列化
是否有一种方法可以为添加的成员指定默认值,或者有一种简单的方法可以在XML中缺少这些成员时忽略它们?它应该反序列化很好,它将只使用默认构造函数初始化项。因此它们将保持不变。来自 最佳实践为确保正确的版本控制行为,在不同版本之间修改类型时,请遵循以下规则:
- 添加新的序列化字段时,应用OptionalFieldAttribute 属性
- 从字段中删除NonSerializedAttribute属性时 在以前的版本中不可序列化),请应用 OptionalFieldAttribute属性
- 对于所有可选字段,使用 可接受序列化回调,除非默认值为0或null
使用系统;
使用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属性更改数据成员的顺序
- 重命名数据成员李>
- 更改数据成员的数据协定李>
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;
}
}