在数据库中,我们有一个包含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文档之前我不知道命名空间是什么,所以我不知道如何继续。
所以我的问题是:我正在尝试做什么甚至可能?我是在正确的轨道上吗?是否有更好的解决方案,可能不那么做作?如何让序列化程序处理新的命名空间并反序列化为正确的属性?
答案 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的适当结构。我对它不满意,但它有效并且足够灵活:/