将XML文档反序列化为C#类,具有多个根(xmlns)命名空间

时间:2016-05-06 16:32:42

标签: c# xml-serialization xml-namespaces

我试图将具有不同根命名空间的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]上当前的硬编码命名空间值。

我试过了:

  1. 添加重复的[XmlRoot]属性(不受支持)
  2. 创建一个基类(BaseIndexRoot),从中派生两个实例并使用[XmlRoot]属性装饰这些派生(相同的&#34;不期望&#34;错误)
  3. 一起删除命名空间也会导致&#34;不被预期&#34;错误

2 个答案:

答案 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语句,它确实可以正常工作。不知怎的,我不禁想到有更多的软化方法来解决这个问题。

从好的方面来说,如果特定版本具有不同的属性,派生类现在非常适合反映这一点。