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

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

问题描述

我有以下信息:

1) 使用 XSD.EXE 工具编译为 C# 类的 XML Schema、XSD 文件.

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 Schema 中定义了许多不同的类型.

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 元素名称与类型名称不同,或者您不想进行反射,则可以创建一个 Dictionary 从 XML 中的元素名称映射到 XmlSerializer 对象,但仍使用上面的代码片段查找根元素名称.

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天全站免登陆