Использование XmlSerializer для десериализации расширяемым образом с произвольными элементами - PullRequest
0 голосов
/ 12 декабря 2018

Я использую XmlSerializer для десериализации XML-ответов от удаленного API.Существует ряд стандартных предопределенных классов, и я создал классы для них.Ключевая часть ответа выглядит следующим образом:

<data>
    <employee>
        <name>John Doe</name>
        <id>E1</id>
        ....
    </employee>
    <employee>
        ....
    </employee>
    ....
</data>

или

<data>
    <customer>
        <name>Yoyodyne Systems</name>
        <id>C1</id>
        ....
    </customer>
    <customer>
        ....
    </customer>
    ....
</data>

Итак, у меня есть классы Customer и Employee, а класс Data имеет свойства, определенные следующим образом:

    [XmlElement("customer", typeof(Customer))]
    public List<Customer> Customers { get; set; }

    [XmlElement("employee", typeof(Employee))]
    public List<Employee> Employees { get; set; }

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

Но я хочу сделать это более расширяемым;в частности, могут быть разные экземпляры удаленного приложения с пользовательскими классами, и я не хочу помещать их все в пакет базового кода.Я надеялся, что смогу добавить свойство в класс Data что-то вроде

    public List<object> ExtensionObjects { get; set; }

, а затем использовать конструктор XmlSerializer(Type, Type[]) для предоставления фактических типов, которые я хочу десериализовать.Однако я не знаю, как сказать ему применить это к любым неизвестным тегам или даже указать теги.Например, если у меня есть собственный класс Office, как я могу сделать так, чтобы теги <office>...</office> были десериализованы в него?Есть ли способ, которым я могу сделать это с XmlSerializer, не возвращаясь к синтаксическому анализу всего как XDocument?

Edit:

Одна вещь, которая кажется многообещающейпытается обработать событие OnUnknownElement;Я могу создать для него делегат, а затем передать его через объект XmlDeserializationEvents в вызов метода Deserialize.Теперь мне нужно найти лучший способ превратить эти неизвестные элементы в экземпляры моих классов расширения.

1 Ответ

0 голосов
/ 13 декабря 2018

Я определил один способ сделать это, хотя, конечно, могут быть и лучшие.

Как я упоминал выше, установите обработчик для OnUnknownElement:

XmlDeserializationEvents events = new XmlDeserializationEvents();
events.OnUnknownElement = delegate(object sender, XmlElementEventArgs args)
{
    if (args.Element.Name == "office")
    {
        var data = (Data) args.ObjectBeingDeserialized;
        var item = new Office(args.Element);
        data.ExtensionObjects.Add(item);
    }
};
var response = (Response) serializer.Deserialize(xmlReader, events);

Тогда всевам нужно реализовать конструктор, чтобы превратить этот XmlElement в ваш объект.Может быть, есть более чистый способ сделать это, но, учитывая, что это простой объект (только свойства «name» и «id»), я просто предполагаю, что элемент - это просто список именованных элементов.

public Office(XmlElement e)
{
    var valDict = e.ChildNodes.Cast<XmlNode>().Where(n => n is XmlElement)
                   .ToDictionary(x => x.Name, x => x.InnerText);

    Name = valDict["name"];
    Id = valDict["id"];
}

Если есть лучший способ сделать это, который снова может пройти через XmlSerializer, я не смог его найти, так как не смог преобразовать XmlElement в XmlReader или что-либо еще, что мог бы принять метод Deserialize. Я полагаю,Возможно, я мог бы снова сериализовать его в текст, а затем обработать, но это кажется глупым.

...