在给定XSD的情况下,如何在C#中进行多态反序列化? [英] How to do a polymorphic deserialization in C# given a XSD?
问题描述
我给出以下信息:
1)使用XSD.EXE工具将XML架构XSD文件编译为C#类.
1) A XML Schema, XSD-file, compiled to C# classes using the XSD.EXE tool.
2)RabbitMQ消息队列,包含XML架构中定义的任何类型的XML格式良好的消息.这是两个不同消息的片段:
2) A RabbitMQ message queue containing well formed messages in XML of any type defined in the XML Schema. Here are two snippets of different messages:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<UserReport xmlns=".../v5.1"; ... >
... User report message content...
</UserReport>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CaptureReport xmlns=".../v5.1"; ...>
... Capture report message content...
</CaptureReport>
3)在知道类型的情况下,使用XmlSerializer .Net类进行反序列化的经验.
3) Experience using the XmlSerializer .Net class to deserialize, when the type is known.
问题是当类型未知时,如何反序列化XML到对象的消息.无法实例化XmlSerializer,因为类型未知.
The question is how to deserialize messages from XML to a an object, when the type is unknown. It's not possible to instantiate the XmlSerializer, because the type is unknown.
一种方法是遍历所有可能的类型,直到反序列化成功为止,这是一个不好的解决方案,因为XML架构中定义了许多不同的类型.
One way is to loop through all possible types until deserialiation succeed, which is a bad solution because there are many different types defined in the XML Schema.
还有其他选择吗?
推荐答案
您可以采取几种方法,具体取决于您在XML本身中实现多态性的精确程度.
There are a few approaches you can take depending on how exactly you've achieved your polymorphism in the XML itself.
您可以像这样获得根元素名称:
You could get the root element name like this:
string rootElement = null;
using (XmlReader reader = XmlReader.Create(xmlFileName))
{
while (reader.Read())
{
// We won't have to read much of the file to find the root element as it will be the first one found
if (reader.NodeType == XmlNodeType.Element)
{
rootElement = reader.Name;
break;
}
}
}
然后,您可以像这样通过反射来找到类型(如果您的类在不同的程序集中,则可以根据需要调整反射):
Then you could find the type by reflection like this (adjust reflection as necessary if your classes are in a different assembly):
var serializableType = Type.GetType("MyApp." + rootElement);
var serializer = new XmlSerializer(serializableType);
如果性能很重要,建议您缓存从元素名称到XML序列化器的映射.
You would be advised to cache the mapping from the element name to the XML serializer if performance is important.
如果XML元素名称与类型名称不同,或者您不想进行反射,则可以创建从XML中的元素名称到XmlSerializer
对象的Dictionary
映射.使用上面的代码段查找根元素名称.
If the XML element names are different from the type names, or you don't want to do reflection, you could instead create a Dictionary
mapping from the element names in the XML to the XmlSerializer
objects, but still look-up the root element name using the snippet above.
如果您的XML消息均具有相同的根元素名称,并且通过使用xsi:type标识类型来实现多态,那么您可以执行以下操作:
If your XML messages all have the same root element name, and the polymorphism is achieved by having types identified using xsi:type, then you can do something like this:
using System;
using System.Xml.Serialization;
namespace XmlTest
{
public abstract class RootElement
{
}
public class TypeA : RootElement
{
public string AData
{
get;
set;
}
}
public class TypeB : RootElement
{
public int BData
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(RootElement),
new Type[]
{
typeof(TypeA),
typeof(TypeB)
});
RootElement rootElement = null;
string axml = "<RootElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"TypeA\"><AData>Hello A</AData></RootElement>";
string bxml = "<RootElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"TypeB\"><BData>1234</BData></RootElement>";
foreach (var s in new string[] { axml, bxml })
{
using (var reader = new System.IO.StringReader(s))
{
rootElement = (RootElement)serializer.Deserialize(reader);
}
TypeA a = rootElement as TypeA;
if (a != null)
{
Console.WriteLine("TypeA: {0}", a.AData);
}
else
{
TypeB b = rootElement as TypeB;
if (b != null)
{
Console.WriteLine("TypeB: {0}", b.BData);
}
else
{
Console.Error.WriteLine("Unexpected type.");
}
}
}
}
}
}
请注意XmlSerializer
构造函数的第二个参数,该构造函数是您希望.NET序列化程序了解的其他类型的数组.
Note the second parameter to the XmlSerializer
constructor which is an array of additional types that you want the .NET serializer to know about.
这篇关于在给定XSD的情况下,如何在C#中进行多态反序列化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!