![](/img/trans.png)
[英]How to deserialize xml to object in C#, If some xml missing in the xml
[英]In C# how do I deserialize XML from an older object into the updated object and ignore missing xml elements?
我有一個自定義設置文件,我使用XmlSerializer
序列化/反序列化。 我的對象定義中沒有定義架構且沒有序列化標簽,只是直接對象序列化(盡管如果需要我會添加它們)。
我的問題是我需要向對象添加數據成員。 如果我這樣做,我知道舊的設置文件不會反序列化。
有沒有辦法為添加的成員指定默認值,或者如果XML中缺少這些默認值,可以采用一些簡單的方法來忽略它們?
來自MSDN
最佳實踐要確保正確的版本控制行為,請在從版本到版本修改類型時遵循以下規則:
添加新的序列化字段時,應用OptionalFieldAttribute屬性。
從字段中刪除NonSerializedAttribute屬性(在以前的版本中不可序列化)時,應用OptionalFieldAttribute屬性。
對於所有可選字段,請使用序列化回調設置有意義的默認值,除非可以接受0或null作為默認值。
我試圖模擬你的情況,在新版本的類中有新成員名為Element2。 我的新成員初始化為“這是新成員”這里是完全證明
Test1假設你使用只有一個Element1的Root類的舊定義序列化
使用Root Class的新定義進行序列化和反序列化時的Test2
要以任何方式回答您的問題以提供默認值,您應該使用“OptionalField”
using System;
using System.Runtime.Serialization;
using System.IO;
public class Test
{
[Serializable]
public class Root
{
[OptionalField(VersionAdded = 2)] // As recommended by Microsoft
private string mElement2 = "This is new member";
public String Element1 { get; set; }
public String Element2 { get { return mElement2; } set { mElement2 = value; } }
}
public static void Main(string[] s)
{
Console.WriteLine("Testing serialized with old definition of Root ");
Console.WriteLine(" ");
Test_When_Original_Object_Was_Serialized_With_One_Element();
Console.WriteLine(" ");
Console.WriteLine("Testing serialized with new definition of Root ");
Console.WriteLine(" ");
Test_When_Original_Object_Was_Serialized_With_Two_Element();
Console.ReadLine();
}
private static void TestReadingObjects(string xml)
{
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(Root));
System.IO.Stream stream = new MemoryStream();
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(xml);
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
Root r = (Root)xmlSerializer.Deserialize(stream);
Console.WriteLine(string.Format("Element 1 = {0}", r.Element1));
Console.WriteLine(string.Format("Element 2 = {0}", r.Element2 == null ? "Null" : r.Element2));
}
private static void Test_When_Original_Object_Was_Serialized_With_One_Element()
{
TestReadingObjects(@"<Root> <Element1>1</Element1> </Root>");
}
private static void Test_When_Original_Object_Was_Serialized_With_Two_Element()
{
TestReadingObjects(@"<Root> <Element1>1</Element1> <Element2>2</Element2> </Root>");
}
}
//這是輸出
它應該反序列化很好,它將只使用默認構造函數來初始化項目。 所以它們將保持不變。
您需要使用自定義方法手動處理它並使用適當的屬性標記它們OnSerializing / OnSerialized / OnDeserializing / OnDeserialized並手動確定如何初始化值(如果可以的話)
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializingattribute.aspx http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx http://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializedattribute.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
}
根據您的要求,可以將其簡化為模型(或模型加載器)上的單個方法。
這也可以在反序列化后用於模型驗證。
編輯:您可以使用以下代碼進行序列化
private static void Serialize(Model model)
{
XmlSerializer xs = new XmlSerializer(typeof(Model));
FileStream f = File.Create(@"..\..\v1.xml");
xs.Serialize(f, model);
f.Close();
}
如果你遵循這種模式,它是相當簡單的:
這樣,即使序列化快照是從以前版本的程序集生成的,您也可以成功反序列化以前的數據。
使用[System.ComponentModel.DefaultValueAttribute]
為Serialization定義DefaultValues。
來自MSDN的示例:
private bool myVal=false;
[DefaultValue(false)]
public bool MyProperty {
get {
return myVal;
}
set {
myVal=value;
}
}
因此,如果您使用DeSerialize並且未填充該屬性,則它將使用defaultValue作為Value,您可以使用舊的XML生成新的Object。
如果在新版本中刪除了屬性,則應該通過XMLSerialization沒有任何問題。 (我所知道的)
您可以使用ExtendedXmlSerializer 。 此序列化程序支持反序列化舊版本的xml。 以下是反序列化舊版xml的示例
您甚至可以從一個文件中讀取對象的不同版本。
.NET為序列化/反序列化和版本控制提供了很多功能。
1)用戶DataContract / DataMember屬性和DataContractSerializer
2)根據MSDN這些變化正在打破
3)當具有額外字段的類型被反序列化為具有缺少字段的類型時,將忽略額外信息。
4)當具有缺失字段的類型被反序列化為具有額外字段的類型時,額外字段保留其默認值,通常為零或null。
5)考慮使用IExtensibleDataObject進行版本控制
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.