首页 > 解决方案 > 您可以将旧版本的 xml 反序列化为更新的结构吗

问题描述

我正在尝试更新正在反序列化的对象。取一个对象,重命名,使用部分并添加一个新对象来保存剩余的对象。.NET 中有没有办法将旧 XML 反序列化为新格式?

例如

旧结构:

<objectinfo>
<element1></element1>
<element2></element2>
<element3></element3>
<element4></element4>
</objectinfo>

新结构:

<objinfo>
<element1></element1>
<element2></element2>
</objinfo>  

<newobject>
<element3></element3>
<element4></element4>
</newobject>

注意我XmlSerializer用来反序列化。

标签: c#.netxmldeserializationxmlserializer

解决方案


假设您正在使用XmlSerializer反序列化,如果您<objectinfo>是根 XML 元素的直接子元素,那么反序列化为某些与旧类型相同的DTO类型并手动或通过映射器映射到新对象可以很容易地解决问题。

然而,如果被修改的对象深深地嵌套在被反序列化的对象层次结构中,那么 DTO 策略就不那么方便了,因为XmlSerializer它没有提供通用的代理 DTO 替换机制。在这种情况下,另一种方法是手动处理事件中的未知元素XmlSerializer.UnknownElement

要做到这一点,一般来说,引入以下 XML 反序列化的接口和扩展方法:

public interface IUnknownElementHandler
{
    void OnUnknownElement(object sender, XmlElementEventArgs e);
}

public static partial class XmlSerializationHelper
{
    public static T LoadFromXml<T>(this string xmlString, XmlSerializer serializer = null)
    {
        serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
        using (var reader = new StringReader(xmlString))
            return (T)serializer.Deserialize(reader);
    }

    public static T LoadFromFile<T>(string filename, XmlSerializer serializer = null)
    {
        serializer = serializer ?? new XmlSerializer(typeof(T)).AddUnknownElementHandler();
        using (var reader = new FileStream(filename, FileMode.Open))
            return (T)serializer.Deserialize(reader);
    }
    
    public static XmlSerializer AddUnknownElementHandler(this XmlSerializer serializer)
    {
        serializer.UnknownElement += (o, e) =>
        {
            var handler = e.ObjectBeingDeserialized as IUnknownElementHandler;
            if (handler != null)
                handler.OnUnknownElement(o, e);
        };
        return serializer;
    }
}

然后,假设您的新数据模型看起来像这样,Root顶级对象在哪里并且ContainerType包含正在重组的元素:

[XmlRoot(ElementName = "Root")]
public class Root
{
    public ContainerType ContainerType { get; set; }
}

[XmlRoot(ElementName = "ContainerType")]
public partial class ContainerType 
{
    [XmlElement(ElementName = "objinfo")]
    public Objinfo Objinfo { get; set; }
    [XmlElement(ElementName = "newobject")]
    public Newobject Newobject { get; set; }
}

[XmlRoot(ElementName = "objinfo")]
public class Objinfo
{
    [XmlElement(ElementName = "element1")]
    public string Element1 { get; set; }
    [XmlElement(ElementName = "element2")]
    public string Element2 { get; set; }
}

[XmlRoot(ElementName = "newobject")]
public class Newobject
{
    [XmlElement(ElementName = "element3")]
    public string Element3 { get; set; }
    [XmlElement(ElementName = "element4")]
    public string Element4 { get; set; }
}

添加一个OnUnknownElement处理程序ContainerType如下:

public partial class ContainerType : IUnknownElementHandler
{
    #region IUnknownElementHandler Members

    void IUnknownElementHandler.OnUnknownElement(object sender, XmlElementEventArgs e)
    {
        var container = (ContainerType)e.ObjectBeingDeserialized;
    
        var element1 = e.Element.SelectSingleNode("element1");
        var element2 = e.Element.SelectSingleNode("element2");
        
        if (element1 != null || element2 != null)
        {
            container.Objinfo = container.Objinfo ?? new Objinfo();
            if (element1 != null)
                container.Objinfo.Element1 = element1.InnerText;
            if (element2 != null)
                container.Objinfo.Element2 = element2.InnerText;
        }
        
        var element3 = e.Element.SelectSingleNode("element3");
        var element4 = e.Element.SelectSingleNode("element4");
        if (element3 != null || element4 != null)
        {
            container.Newobject = container.Newobject ?? new Newobject();
            if (element3 != null)
                container.Newobject.Element3 = element3.InnerText;
            if (element4 != null)
                container.Newobject.Element4 = element4.InnerText;
        }
    }

    #endregion
}

然后,当您Root使用上述方法从文件反序列化时LoadFromFile

var root = XmlSerializationHelper.LoadFromFile<Root>(filename);

过时的、未知的 XML 元素将由处理程序进行后ContainerType处理。

演示小提琴在这里


推荐阅读