简体   繁体   English

使用XmlSerializer以可扩展的方式对任意元素进行反序列化

[英]Using XmlSerializer to deserialize in an extensible fashion with arbitrary elements

I am using XmlSerializer to deserialize XML responses from a remote API. 我正在使用XmlSerializer反序列化来自远程API的XML响应。 There are a number of standard predefined classes, and I have built up classes for these. 有许多标准的预定义类,我已经为这些类建立了类。 The key part of the response looks like this: 响应的关键部分如下所示:

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

or 要么

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

So I have Customer and Employee classes, and the Data class has properties defined like this: 所以我有Customer和Employee类,而Data类具有如下定义的属性:

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

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

The data element only contains one type of tag per result, and I generally know which one is expected to be returned from a given request, so I just access that property and ignore the other ones which are left empty. 数据元素每个结果只包含一种类型的标记,而且我通常知道期望从给定请求中返回哪种标记,因此我只访问该属性,而忽略其他留空的属性。

But I want to make this more extensible; 但是我想使它更具可扩展性。 in particular there may be different instances of the remote application with custom classes in them, and I don't want to put them all into the base code package. 特别是远程应用程序中可能有不同的实例,其中包含自定义类,我不想将它们全部放入基本代码包中。 I was hoping I could add a property to the Data class something like 我希望可以将属性添加到Data类中,例如

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

and then use the XmlSerializer(Type, Type[]) constructor to supply the actual types I want to deserialize. 然后使用XmlSerializer(Type, Type[])构造函数提供要反序列化的实际类型。 However, I don't know how to tell it to apply this to any unknown tags, or even to specify the tags. 但是,我不知道如何告诉它将其应用于任何未知标签,甚至指定标签。 For instance, if I have a custom Office class, how can I make it that <office>...</office> tags get deserialized into it? 例如,如果我有一个自定义Office类,如何使<office>...</office>标记反序列化到其中? Is there a way I can do this with XmlSerializer without having to go all the way back to parsing everything as an XDocument? 有没有一种方法可以用XmlSerializer做到这一点,而不必一直回到将所有内容解析为XDocument的方式?

Edit: 编辑:

One thing that seems promising is trying to handle the OnUnknownElement event; 似乎有希望的一件事是尝试处理OnUnknownElement事件。 I can create a delegate for it and then pass it in via an XmlDeserializationEvents object to the Deserialize method call. 我可以为其创建一个委托,然后将其通过XmlDeserializationEvents对象传递给Deserialize方法调用。 Now I just have to figure out the best way to turn those unknown elements into instances of my extension classes. 现在,我只需要找出将那些未知元素转换为扩展类实例的最佳方法。

I have determined one way to do this, though of course there may be better ones. 我已经确定了执行此操作的一种方法,尽管当然可能会有更好的方法。

As I mentioned above, set up a handler for OnUnknownElement: 如上所述,为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);

Then all you need do is implement the constructor to turn that XmlElement into your object. 然后,您所需要做的就是实现构造函数,以将XmlElement转换为对象。 There may be a cleaner way to do it, but considering this is a simple object (just "name" and "id" properties) I'm just assuming the element is just a flat list of named elements. 可能有一种更简洁的方法,但是考虑到这是一个简单的对象(只是“ 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"];
}

If there's a better way to do this that can go through the XmlSerializer again, I was unable to find it, since I couldn't convert an XmlElement to an XmlReader or anything else that the Deserialize method could take in. I suppose I could probably serialize it to text again and then reparse it, but that seems kludgy. 如果有更好的方法可以再次通过XmlSerializer,则无法找到它,因为我无法将XmlElement转换为XmlReader或Deserialize方法可以接受的任何其他功能。我想我可能再次将其序列化为文本,然后重新解析,但这似乎很麻烦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM