【问题标题】:Using XmlSerializer to deserialize in an extensible fashion with arbitrary elements使用 XmlSerializer 以可扩展的方式对任意元素进行反序列化
【发布时间】:2018-12-12 19:07:51
【问题描述】:

我正在使用 XmlSerializer 反序列化来自远程 API 的 XML 响应。有许多标准的预定义类,我已经为它们建立了类。响应的关键部分如下所示:

<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 类,我怎样才能将 &lt;office&gt;...&lt;/office&gt; 标签反序列化到其中?有没有一种方法可以让我使用 XmlSerializer 做到这一点,而不必一直回到将所有内容解析为 XDocument?

编辑:

似乎很有希望的一件事是尝试处理 OnUnknownElement 事件。我可以为它创建一个委托,然后通过 XmlDeserializationEvents 对象将其传递给 Deserialize 方法调用。现在我只需要找出将这些未知元素转化为我的扩展类实例的最佳方法。

【问题讨论】:

    标签: c# xmlserializer


    【解决方案1】:

    我已经确定了一种方法来做到这一点,当然还有更好的方法。

    如上所述,为 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 方法可以接受的任何其他东西。我想我可能可以再次将其序列化为文本,然后重新解析它,但这似乎很笨拙。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多