我试图将具有不同根命名空间的XML文档反序列化为C#类。
简而言之,我想反序列化类似xml文档的多个版本,如下所示:
<IndexRoot Code="0664" xmlns="http://tempuri/2012/1.0">
<Name>Foo</Name>
<Color>blue</Color>
...
</IndexRoot>
<IndexRoot Code="0678" xmlns="http://tempuri/2012/2.0">
<Name>Bar</Name>
<Character>Smurf</Character>
</IndexRoot>
每个版本显然可以在其下面具有不同的元素,并且虽然大多数元素是相同的但是存在一些差异。在上面的示例中,Name属性在每个版本中都可用,而Color / Character对每个版本都是唯一的。
理想情况下,我想将它抽象为一个简单的函数,它给了我一个反映的具体类。像这样:
public IndexRoot Get(string fileName) {
var doc = XDocument.Load(fileName);
return xmlSerializer.Deserialize<IndexRoot>(doc);
}
在我当前的设置中,这会失败,因为需要在根元素上提供单个命名空间以使反序列化器工作:
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/2.0")]
public class IndexRoot
{
[XmlAttribute("Code")]
public string Code { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
}
正如您所看到的,硬编码命名空间适用于2.0版本,但对于其他版本将失败,例外情况如下:&#34; IndexRoot xmlns =&#39; http://tempuri/2012/1.0&#39;没想到。&#34;
问题:如何将XML反序列化为C#对象,并考虑多个根名称空间?
理想情况下,这将反映为每个版本的具体类型。但是我甚至会满足于获得一个基础课#34;与共同的共享属性。无论哪种方式,我目前都在使用[XmlRoot]上当前的硬编码命名空间值。
我试过了:
答案 0 :(得分:0)
我通常这样做
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication91
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<IndexRoot Code=\"0664\" xmlns=\"http://tempuri/2012/1.0\">" +
"<Name>Foo</Name>" +
"<Color>blue</Color>" +
"</IndexRoot>";
XDocument doc = XDocument.Parse(xml);
XElement indexRoot = (XElement)doc.FirstNode;
XNamespace ns = indexRoot.Name.Namespace;
string name = indexRoot.Element(ns + "Name").Value;
XElement indexRoot2 = doc.Descendants().Where(x => x.Name.LocalName == "IndexRoot").FirstOrDefault();
}
}
}
答案 1 :(得分:0)
我能够解决使用相同结构反序列化XML文档的问题,但具有不同的命名空间,具有以下结构。
首先,我为每个特定版本创建了派生类,并使用命名空间对其进行了修饰:
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/1.0")]
public class IndexRootV10 : IndexRoot { }
[Serializable, XmlRoot(ElementName = "IndexRoot", Namespace = "http://tempuri/2012/2.0")]
public class IndexRootV20 : IndexRoot { }
public class IndexRoot
{
[XmlAttribute("Code")]
public string Code { get; set; }
[XmlElement(ElementName = "Code")]
public string Code { get; set; }
}
我需要做的就是简单地修改deserialize函数以确定要应用的版本(dervied类):
public IndexRoot Get(string fileName) {
var doc = XDocument.Load(fileName);
switch (doc.Root?.Name.NamespaceName)
{
case "http://tempuri/2012/1.0":
response = xmlSerializer.Deserialize<IndexRootV10>(doc);
break;
case "http://tempuri/2012/2.0":
response = xmlSerializer.Deserialize<IndexRootV20>(doc);
break;
default:
throw new NotSupportedException($"Unsupported xmlns namespace (version): {doc.Root.Name.NamespaceName}");
}
}
虽然tis是我最不高兴的部分,但由于硬编码的switch语句,它确实可以正常工作。不知怎的,我不禁想到有更多的软化方法来解决这个问题。
从好的方面来说,如果特定版本具有不同的属性,派生类现在非常适合反映这一点。