使用 XmlSerializer 以可扩展的方式反序列化任意元素
Using XmlSerializer to deserialize in an extensible fashion with arbitrary elements
我正在使用 XmlSerializer 反序列化来自远程 API 的 XML 响应。有许多标准的预定义 classes,我已经为这些建立了 classes。响应的关键部分如下所示:
<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 classes,数据 class 的属性定义如下:
[XmlElement("customer", typeof(Customer))]
public List<Customer> Customers { get; set; }
[XmlElement("employee", typeof(Employee))]
public List<Employee> Employees { get; set; }
数据元素每个结果只包含一种类型的标签,我通常知道给定请求返回的是哪一种,所以我只访问 属性 并忽略其他那些留空了。
但我想让它更具可扩展性;特别是可能有不同的远程应用程序实例,其中包含自定义 classes,我不想将它们全部放入基本代码包中。我希望我可以将 属性 添加到数据 class 之类的东西
public List<object> ExtensionObjects { get; set; }
然后使用 XmlSerializer(Type, Type[])
构造函数提供我想要反序列化的实际类型。但是,我不知道如何告诉它把它应用到任何未知的标签,甚至指定标签。例如,如果我有一个自定义 Office class,我怎样才能将 <office>...</office>
标签反序列化到其中?有没有一种方法可以使用 XmlSerializer 执行此操作,而不必一直返回到将所有内容解析为 XDocument?
编辑:
似乎有希望的一件事是尝试处理 OnUnknownElement 事件;我可以为它创建一个委托,然后通过 XmlDeserializationEvents 对象将其传递给 Deserialize 方法调用。现在我只需要找出将这些未知元素转换为我的扩展实例的最佳方法 classes.
我已经确定了一种方法,当然还有更好的方法。
正如我上面提到的,为 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 方法可以接受的任何其他内容。我想我可能可以再次将它序列化为文本,然后重新解析它,但这看起来很笨拙。
我正在使用 XmlSerializer 反序列化来自远程 API 的 XML 响应。有许多标准的预定义 classes,我已经为这些建立了 classes。响应的关键部分如下所示:
<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 classes,数据 class 的属性定义如下:
[XmlElement("customer", typeof(Customer))]
public List<Customer> Customers { get; set; }
[XmlElement("employee", typeof(Employee))]
public List<Employee> Employees { get; set; }
数据元素每个结果只包含一种类型的标签,我通常知道给定请求返回的是哪一种,所以我只访问 属性 并忽略其他那些留空了。
但我想让它更具可扩展性;特别是可能有不同的远程应用程序实例,其中包含自定义 classes,我不想将它们全部放入基本代码包中。我希望我可以将 属性 添加到数据 class 之类的东西
public List<object> ExtensionObjects { get; set; }
然后使用 XmlSerializer(Type, Type[])
构造函数提供我想要反序列化的实际类型。但是,我不知道如何告诉它把它应用到任何未知的标签,甚至指定标签。例如,如果我有一个自定义 Office class,我怎样才能将 <office>...</office>
标签反序列化到其中?有没有一种方法可以使用 XmlSerializer 执行此操作,而不必一直返回到将所有内容解析为 XDocument?
编辑:
似乎有希望的一件事是尝试处理 OnUnknownElement 事件;我可以为它创建一个委托,然后通过 XmlDeserializationEvents 对象将其传递给 Deserialize 方法调用。现在我只需要找出将这些未知元素转换为我的扩展实例的最佳方法 classes.
我已经确定了一种方法,当然还有更好的方法。
正如我上面提到的,为 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 方法可以接受的任何其他内容。我想我可能可以再次将它序列化为文本,然后重新解析它,但这看起来很笨拙。