Десериализовать XML-документ в класс C # с несколькими корневыми (xmlns) пространствами имен

Я пытаюсь десериализовать 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 доступен в каждой версии, а цвет / символ уникальны для каждой версии.

В идеале я хочу абстрагировать это до простой функции, которая дает мне отраженный конкретный класс. Вот так:

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, но не будет работать для других версий за исключением: "IndexRoot xmlns = 'http://tempuri/2012/1.0" не ожидалось. "

Вопрос: как я могу десериализовать XML в объект C #, принимая во внимание несколько корневых пространств имен?

В идеале это должно отражаться на конкретном типе для каждой версии. Но я даже соглашусь на получение «базового класса» с общими общими свойствами. В любом случае, в настоящее время я застрял с текущим жестко заданным значением пространства имен в [XmlRoot].

Я пытался:

  1. Добавление повторяющихся атрибутов [XmlRoot] (что не поддерживается)
  2. Создайте базовый класс (BaseIndexRoot), создав от него два экземпляра и украсив эти производные атрибутом [XmlRoot] (та же ошибка "не ожидалось")
  3. Удаление всего пространства имен также приводит к ошибке "не ожидалось".

person Juliën    schedule 06.05.2016    source источник


Ответы (2)


Я обычно так делаю

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();

        }


    }

}
person jdweng    schedule 06.05.2016

Я смог решить проблему десериализации 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; }
}

Все, что мне нужно было сделать после этого, - просто изменить функцию десериализации, чтобы определить, какую версию (класс 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}");
    }
}

Хотя это часть, которая меня меньше всего радует, из-за жестко запрограммированного оператора switch, она действительно работает правильно. Почему-то я не могу не думать, что есть более сложные способы решить эту проблему.

С другой стороны, если конкретная версия имеет другие свойства, производные классы теперь идеально подходят для отражения этого.

person Juliën    schedule 21.05.2016