在给定XSD的情况下,如何在C#中进行多态反序列化? [英] How to do a polymorphic deserialization in C# given a XSD?

查看:104
本文介绍了在给定XSD的情况下,如何在C#中进行多态反序列化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我给出以下信息:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆