Как десериализовать XML, который имеет 2 элемента с одинаковыми именами, но элементы имеют разные типы данных? - PullRequest
2 голосов
/ 09 ноября 2019

Я работаю с API, который возвращает XML. Если я вызову API с одним набором параметров, я вернусь к XML, который выглядит примерно так:

Тип 1:

<root>
    <foos>
      <foo>some</foo>
      <foo>text</foo>
      <foo>here</foo>
    </foos>
</root>

Но если я вызову API с другим наборомпараметры, я получаю обратно XML, который выглядит примерно так:

Тип 2:

<root>
    <foos>
        <foo>
            <fooName>some</fooName>
            <fooId>1</fooId>
            <fooDate>11-8-2019</fooDate>
        </foo>
        <foo>
            <fooName>text</fooName>
            <fooId>2</fooId>
            <fooDate>11-9-2019</fooDate>
        </foo>
        <foo>
            <fooName>here</fooName>
            <fooId>3</fooId>
            <fooDate>11-10-2019</fooDate>
        </foo>
    </foos>
</root>

Я хотел бы смоделировать это как один объект C #, если это возможно ... так, чтобы яможет десериализовать любой XML с помощью чего-то подобного:

private root Deserialize(string xmlData)
{
    var serializer = new XmlSerializer(typeof(root));
    using (var reader = new StringReader(xmlData))
    {                
        return (root)serializer.Deserialize(reader);                
    }            
}

Я пытался моделировать классы XML следующим образом:

[XmlRoot("root")]
public class Root
{
    [XmlAttribute(AttributeName = "foos")]
    public List<string> foos { get; set; }              
}

Когда класс выглядит следующим образом, я могу десериализовать xmlData для Type1 (и я получаю список строк). Но когда я пытаюсь десериализовать xmlData для типа 2, все становится пустым ...

И если я моделирую класс XML следующим образом:

[XmlRoot("root")]
public class Root
{
    [XmlAttribute(AttributeName = "foos")]
    public List<Foo> foos { get; set; }              
}

[XmlRoot("foo")]
public class Foo
{
    [XmlAttribute(AttributeName = "fooName")]
    public string FooName { get; set; }

    [XmlAttribute(AttributeName = "fooId")]
    public string FooId { get; set; }

    [XmlAttribute(AttributeName = "fooDate")]
    public string FooDate { get; set; }               
}

Тогда я могу десериализовать xmlData дляТип 2 (и я получаю список сложных объектов). Но, конечно, это не работает для xmlData для типа 1.

Есть ли способ моделировать классы в C # для обработки обоих случаев?

1 Ответ

1 голос
/ 09 ноября 2019

Что вы можете сделать, это изменить Foo, чтобы он мог захватывать как текстовое значение, так и ожидаемые вложенные дочерние элементы, добавив свойство со строковым значением и пометив его [XmlText] следующим образом:

[XmlRoot("root")]
public class Root
{
    [XmlArray("foos")]
    [XmlArrayItem("foo")]
    public List<Foo> foos { get; set; }              
}

[XmlRoot("foo")]
public class Foo
{
    [XmlElement("fooName")]
    public string FooName { get; set; }

    [XmlElement("fooId")]
    public string FooId { get; set; }

    [XmlElement("fooDate")]
    public string FooDate { get; set; }               

    [XmlText]
    public string Value { get; set; }
}

Примечания:

  1. XmlTextAttribute

    Указывает XmlSerializer, что элементдолжен обрабатываться как текст XML, когда класс, который его содержит, сериализован или десериализован.

  2. Foo теперь сможет связываться с смешанным содержимым такие узлы, как

    <foo>Some text<fooName>text</fooName>
      <fooId>2</fooId>
      <fooDate>11-9-2019</fooDate>
    </foo>
    
  3. В вашей существующей модели данных Root и Foo, которые я исправил выше, есть некоторые ошибки, в том числе:

    • <fooName>, <fooId> и <fooDate> являются дочерними элементами, а не атрибутами, поэтому их соответствующие свойства должны быть помечены [XmlElement].

    • foos связан с последовательностью с внешним элементом контейнера <foos> и внутренними последовательными элементами <foo> и поэтому должен быть помечен [XmlArray].

Демонстрационная скрипка здесь .

...