如何使用相同的xml元素和属性名称的两种不同类型反序列化xml?

时间:2014-06-30 19:04:34

标签: c# serialization xml-serialization

在数据库中,我们有一个包含2个验证模式的xml字段;旧的没有命名空间,新的命名空间。原因是我们必须对其中一个属性进行版本控制。以下是差异的示例:

版本1

<PropertyA>
  <PropertyA1>false</PropertyA1>
  <PropertyA2>3.23</PropertyA2>
</PropertyA>

第2版

<ts:PropertyA xmlns:ts="http://www.example.com/v2">
  <ts:PropertyA1>false</ts:PropertyA2>
  <ts:PropertyA2>
    <ts:PropertyA2a>
        <ts:PropertyA2a1>City 1</ts:PropertyA2a1>
        <ts:PropertyA2a2>3.23</ts:PropertyA2a2>
    </ts:PropertyA2a>
    <ts:PropertyA2b>
        <ts:PropertyA2b1>City 2</ts:PropertyA2b1>
        <ts:PropertyA2b2>1.21</ts:PropertyA2b2>
    </ts:PropertyA2b>
  </ts:PropertyA2>
</ts:PropertyA>

基本上,我们只为PropertyA2 ...

创建多个选项

所以现在的问题是反序列化。此对象需要反序列化为应用程序代码中的同一数据对象,问题是元素名称相同,因此序列化程序显然无法确定要反序列化的对象,因为有时数据库将返回版本1,有时它将返回第2版。

以下是用于序列化的数据类的一个示例,以及我当前的方法并不完全有效:

[Serializable]
public class MyDataClass
{
    // ... other stuff

    [XmlElement(Name = "PropertyA", typeof(V1.PropertyA), Namespace = "")]
    public V1.PropertyA PropertyAV1 { get ;set; }

    [XmlElement(Name = "PropertyA", typeof(V2.PropertyA), Namespace = "http://www.example.com/v2")]
    public V2.PropertyA PropertyAV2 { get; set; }
}

[Serializable]
public class V1.PropertyA
{
    public bool PropertyA1 { get; set; }

    public decimal PropertyA2 { get; set; }
}

[Serializable]
public class V2.PropertyA
{
    public bool PropertyA1 { get; set; }

    public List<SomeOtherThing> PropertyA2 { get; set; }
}

当我去反序列化V1时,它工作正常。当我去反序列化V2时,我得到一个错误Did not expect <ts:PropertyA xmlns:ts="http://www.example.com/v2">所以我认为在deserialize方法中我缺少一个参数:

public MyDataClass Deserialize(string xml)
{
    var s = new XmlSerializer(typeof (MyDataClass));
    MyDataClass info = null;
    using (var r = new StringReader(xml))
    {
        info = (MyDataClass) s.Deserialize(r);
    }
    return info;
}

我相信你可以在序列化程序中设置预期的命名空间,但由于在实际检查xml文档之前我不知道命名空间是什么,所以我不知道如何继续。

所以我的问题是:我正在尝试做什么甚至可能?我是在正确的轨道上吗?是否有更好的解决方案,可能不那么做作?如何让序列化程序处理新的命名空间并反序列化为正确的属性?

2 个答案:

答案 0 :(得分:1)

你不能。

这里的问题是您必须根据单个XMLSchema对MyDataClass进行硬编码。如果XMLSchema改变,MyDataClass不再是XMLSerializer的反序列化方法的有效目标,这就是为什么你得到'Did not expect ...'错误消息。在这种情况下,当读取V2 xml数据流时,deserialize方法尝试用&lt; ts:PropertyA2&gt;的内容填充MyDataClass#PropertyAV1。并且没有办法告诉它改为填充MyDataClass#PropertyAV2。即使有办法实现这一点,你也会在MyDataClass类型的对象中遇到MyDataClass#PropertyAV1的未定义值。

因此,手头的问题有两种解决方案:

a)坚持使用XMLSerializer并定义类MyDataClass,如此

    public class MyDataClass 
    {    
        // only one Property here, as there's only one root element in the xml
        // and this single Property is not bound to a specific XML element
        [XmlAnyElement]    
        public PropertyA PropertyA { get ;set; }
    }

然后,您必须自己分析PropertyA的内容并围绕它构建一些逻辑,请参阅此处了解更多详细信息:

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlanyelementattribute.aspx

b)分配XMLSerializer,使用XMLReader读取XML数据流并自己完成xml的所有解析,同时添加逻辑来创建相应的C#对象,具体取决于您读取的xml类型。 / p>

显然,这两种解决方案都需要在C#端进行更多编码,但是使用解决方案b)您将有机会获得性能优势,因为XMLSerializer #deserialize最有可能构建一个DOM树来创建C#对象, XMLReader没有。

答案 1 :(得分:1)

似乎我试图做的事情要么无法实现,要么没有合适级别的xml fu看到这个帖子:(。

所以无论如何,我最终做的是在数据库中添加一个额外的列,其中包含xml契约的版本号。由于那里的一切都是一样的,我只称它为V1。

然后我将该信息读入应用程序代码并使用版本号来驱动工厂。基本上,如果是v1,那么反序列化为此,如果是v2,则反序列化到另一个东西。

当然,为了支持这一点,我只是创建了一个新的数据对象,它具有支持v2的适当结构。我对它不满意,但它有效并且足够灵活:/