如何在重命名某些属性后反序列化旧 XML 文件?
How to deserialize old XML files after some properties have been renamed?
为简单起见,假设我有这个 class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
我使用以下两种方法 serialization/deserialization:
public void Serialize(Person person, string outputFilePath)
{
using (var stream =
new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
{
var serializer = new XmlSerializer(person.GetType());
serializer.Serialize(stream, person);
}
}
public Person Deserialize(string filePath)
{
using (var stream = new FileStream(filePath, FileMode.Open))
{
var serializer = new XmlSerializer(typeof(Person));
return (Person)serializer.Deserialize(stream);
}
}
现在,假设我将一些 Person
对象序列化为 XML 文件,然后重命名 Person
class 中的一个或多个 属性:
// Renamed from `Name`
public string FullName { get; set; }
我的目标是允许程序仍然反序列化那些具有 Name
元素的旧 XML 文件,而不是 FullName
XmlElementAttribute
在这里无济于事:
// If I do this, I can't deserialize from files created after
// the property has been renamed.
[XmlElement("Name")]
public string FullName { get; set; }
如果我可以支持多个旧名称,也许使用一些属性,那将是理想的。例如,
[XmlAlternateDeserializationElement("Name")]
[XmlAlternateDeserializationElement("Label")]
public string FullName { get; set; }
但任何其他方式就足够了。我怎样才能做到这一点?
下面显示了如何在一个或多个 属性 名称发生更改时反序列化一个 XML 文件名。
注意:在下面的代码中,当XML被序列化时,它将以较新的格式保存。
XML - 原创
<Person>
<Id>1</Id>
<Name>John Smith</Name>
<City>Some City</City>
</Person>
XmlPersonOriginal:
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPersonOriginal
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string StateOrProvince { get; set; }
}
}
序列化
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
using (System.IO.TextWriter xmlStream = new System.IO.StreamWriter(xmlFilename))
{
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(string.Empty, "urn:none");
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
//write to XML file
serializer.Serialize(xmlStream, obj, ns);
}
}
用法(序列化):
string filename = @"C:\Temp\Person.xml";
XmlPersonOriginal person = new XmlPersonOriginal() { Id = 1, Name = "John Smith", City = "Some City"};
Helper.SerializeObjectToXMLFile(person, filename);
反序列化
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
T rObject = default(T);
using (System.IO.StreamReader xmlStream = new System.IO.StreamReader(xmlFilename))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
rObject = (T)serializer.Deserialize(xmlStream);
}
return rObject;
}
用法(反序列化):
string filename = @"C:\Temp\Person.xml";
XmlPersonOriginal person = Helper.DeserializeXMLFileToObject<XmlPersonOriginal>(filename);
现在,如果决定将 属性 名称从 Name
更改为 FullName
,则可以创建一个新的 class (XmlPerson_v2)继承自原始 (XmlPersonOriginal) 并覆盖任何已更改的 属性 名称。为了能够覆盖 属性,将关键字“virtual”添加到 属性.
XmlPersonOriginal:
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPersonOriginal
{
public int Id { get; set; }
public virtual string Name { get; set; }
public string City { get; set; }
public string StateOrProvince { get; set; }
}
}
XmlPerson_v2:
using System;
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPerson_v2 : XmlPersonOriginal
{
public string FullName { get; set; }
#pragma warning disable CS0809
[XmlIgnore]
[Obsolete("Name is deprecated, use FullName instead.")]
public override string Name
{
get
{
return base.Name;
}
set
{
FullName = value;
}
}
}
}
用法(序列化):
string filename = @"C:\Temp\PersonModified.xml";
XmlPerson_v2 person = new XmlPerson_v2() { Id = 1, FullName = "John Smith", City = "Some City"};
Helper.SerializeObjectToXMLFile(person, filename);
用法(反序列化):
string filename = @"C:\Temp\Person.xml";
XmlPerson_v2 person = Helper.DeserializeXMLFileToObject<XmlPerson_v2>(filename);
用法(反序列化):
string filename = @"C:\Temp\PersonModified.xml";
XmlPerson_v2 person = Helper.DeserializeXMLFileToObject<XmlPerson_v2>(filename);
资源
- How to mark a method as obsolete or deprecated?
- ObsoleteAttribute Class
- Suppress code analysis violations
您可以利用 XmlSerializer
的 UnknownElement / UnknownAttribute 事件来处理旧的元素名称或属性名称:
var serializer = new XmlSerializer(typeof(Person));
serializer.UnknownElement += (sender, e) =>
{
var person = (Person)e.ObjectBeingDeserialized;
if (e.Element.Name == "Name")
{
person.FullName = e.Element.InnerText;
}
};
这可以保持您的实际数据 类 干净并将兼容性代码集中在序列化方法中。
为简单起见,假设我有这个 class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
我使用以下两种方法 serialization/deserialization:
public void Serialize(Person person, string outputFilePath)
{
using (var stream =
new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
{
var serializer = new XmlSerializer(person.GetType());
serializer.Serialize(stream, person);
}
}
public Person Deserialize(string filePath)
{
using (var stream = new FileStream(filePath, FileMode.Open))
{
var serializer = new XmlSerializer(typeof(Person));
return (Person)serializer.Deserialize(stream);
}
}
现在,假设我将一些 Person
对象序列化为 XML 文件,然后重命名 Person
class 中的一个或多个 属性:
// Renamed from `Name`
public string FullName { get; set; }
我的目标是允许程序仍然反序列化那些具有 Name
元素的旧 XML 文件,而不是 FullName
XmlElementAttribute
在这里无济于事:
// If I do this, I can't deserialize from files created after
// the property has been renamed.
[XmlElement("Name")]
public string FullName { get; set; }
如果我可以支持多个旧名称,也许使用一些属性,那将是理想的。例如,
[XmlAlternateDeserializationElement("Name")]
[XmlAlternateDeserializationElement("Label")]
public string FullName { get; set; }
但任何其他方式就足够了。我怎样才能做到这一点?
下面显示了如何在一个或多个 属性 名称发生更改时反序列化一个 XML 文件名。
注意:在下面的代码中,当XML被序列化时,它将以较新的格式保存。
XML - 原创
<Person>
<Id>1</Id>
<Name>John Smith</Name>
<City>Some City</City>
</Person>
XmlPersonOriginal:
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPersonOriginal
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string StateOrProvince { get; set; }
}
}
序列化
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
using (System.IO.TextWriter xmlStream = new System.IO.StreamWriter(xmlFilename))
{
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(string.Empty, "urn:none");
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
//write to XML file
serializer.Serialize(xmlStream, obj, ns);
}
}
用法(序列化):
string filename = @"C:\Temp\Person.xml";
XmlPersonOriginal person = new XmlPersonOriginal() { Id = 1, Name = "John Smith", City = "Some City"};
Helper.SerializeObjectToXMLFile(person, filename);
反序列化
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
T rObject = default(T);
using (System.IO.StreamReader xmlStream = new System.IO.StreamReader(xmlFilename))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
rObject = (T)serializer.Deserialize(xmlStream);
}
return rObject;
}
用法(反序列化):
string filename = @"C:\Temp\Person.xml";
XmlPersonOriginal person = Helper.DeserializeXMLFileToObject<XmlPersonOriginal>(filename);
现在,如果决定将 属性 名称从 Name
更改为 FullName
,则可以创建一个新的 class (XmlPerson_v2)继承自原始 (XmlPersonOriginal) 并覆盖任何已更改的 属性 名称。为了能够覆盖 属性,将关键字“virtual”添加到 属性.
XmlPersonOriginal:
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPersonOriginal
{
public int Id { get; set; }
public virtual string Name { get; set; }
public string City { get; set; }
public string StateOrProvince { get; set; }
}
}
XmlPerson_v2:
using System;
using System.Xml.Serialization;
namespace XmlSerializationRenamedProperty
{
[XmlRoot(ElementName = "Person", IsNullable = false)]
public class XmlPerson_v2 : XmlPersonOriginal
{
public string FullName { get; set; }
#pragma warning disable CS0809
[XmlIgnore]
[Obsolete("Name is deprecated, use FullName instead.")]
public override string Name
{
get
{
return base.Name;
}
set
{
FullName = value;
}
}
}
}
用法(序列化):
string filename = @"C:\Temp\PersonModified.xml";
XmlPerson_v2 person = new XmlPerson_v2() { Id = 1, FullName = "John Smith", City = "Some City"};
Helper.SerializeObjectToXMLFile(person, filename);
用法(反序列化):
string filename = @"C:\Temp\Person.xml";
XmlPerson_v2 person = Helper.DeserializeXMLFileToObject<XmlPerson_v2>(filename);
用法(反序列化):
string filename = @"C:\Temp\PersonModified.xml";
XmlPerson_v2 person = Helper.DeserializeXMLFileToObject<XmlPerson_v2>(filename);
资源
- How to mark a method as obsolete or deprecated?
- ObsoleteAttribute Class
- Suppress code analysis violations
您可以利用 XmlSerializer
的 UnknownElement / UnknownAttribute 事件来处理旧的元素名称或属性名称:
var serializer = new XmlSerializer(typeof(Person));
serializer.UnknownElement += (sender, e) =>
{
var person = (Person)e.ObjectBeingDeserialized;
if (e.Element.Name == "Name")
{
person.FullName = e.Element.InnerText;
}
};
这可以保持您的实际数据 类 干净并将兼容性代码集中在序列化方法中。