将1个XML的c#XML反序列化到不同的类 [英] c# XML deserialization of 1 XML to different classes

查看:125
本文介绍了将1个XML的c#XML反序列化到不同的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想有效地管理多个客户标准。

I would like to manage multiple customer standards efficiently.

如果我打开(反序列化)XML,我想确定反序列化过程中使用了哪些类。选择另一个类基本上意味着从另一个角度(视图)看XML。

If I open (deserialize) an XML, I want to determine which classes are used during deserialisation. Choosing another class basically means looking at the XML from an other perspective (view).

我现在所拥有的:
我有一个类 Project ,其中包含一些属性和方法。
我能够将motor的实例序列化为XML,这可以正常工作。
反序列化也可以。

What I have right now: I have a class Project which has some properties and methods. I am able to serialize instances of motor to XML, this works fine. Also deserialization works fine.

现在,我创建一个新类 ProjectCustomerA ,该类派生自基本类 Project 。我覆盖了 ProjectCustomerA 上的某些方法,并且将来可能会添加一些方法。

Now I create a new class ProjectCustomerA, which is derived from the base class Project. I overwrite some of the methods on ProjectCustomerA and might add some in the future.

两个类均 Project ProjectCustomerA 共享相同的 XmlType [可序列化的XmlType( Project)] )。

Both class Project and ProjectCustomerA share the same XmlType ([Serializable, XmlType("Project")]).

现在,当我反序列化XML时,我得到一个错误,即两个类都使用相同的 XmlType 这是不可能的。

Now when I deserialize an XML I get an error that both classes use the same XmlType and that this is not possible.

下面是我收到的消息(它最初是荷兰语,我翻译了):

Below is the message I get (it was originally in Dutch and I translated):


System.InvalidOperationException HResult = 0x80131509 ... Inner
Exception 1:InvalidOperationException:类型C4M_Data.C4M_Project
和C4M_Data_customer.C4M_Project_Customer都使用命名空间中的XML类型名
Project。使用XML属性为类型定义唯一的
XML名称和/或-namespace。

System.InvalidOperationException HResult=0x80131509 ... Inner Exception 1: InvalidOperationException: The types C4M_Data.C4M_Project and C4M_Data_customer.C4M_Project_Customer both use the XML-typename, Project, from namespace . Use XML-attributes to define a unique XML-name and/or -namespace for the type.

我的问题是如何读取(反序列化)相同的XML,并让我控制在此过程中在应用程序中实例化了哪些类?

My question is how to read (deserialize) the same XML and let me control what classes are instantiated in my application during this process?

我目前的想法是,不同的类型(如果需要,都使用相同的基类)应导致具有相同根元素和名称空间的XML。 XML应该始终看起来相同。然后,我需要控制/强制 XmlSerializer 反序列化为所需的类型,而不管根元素名称和命名空间如何。

My current idea is that different types (all the same baseclass if needed) should result in an XML with the same root element and namespace. The XML should always look the same. Then I need to control / force the XmlSerializer to deserialize to the type I want, regardless of the root element name and namespace. Is this possible?

推荐答案

在类型层次结构中不能有多个具有相同的 [XmlType] 属性。如果这样做, XmlSerializer 构造函数将抛出您所看到的异常,说明:

You cannot have multiple types in the type hierarchy have identical [XmlType] attributes. If you do, the XmlSerializer constructor will throw the exception you have seen, stating:


使用XML属性为类型定义唯一的XML名称和/或-名称空间。

Use XML-attributes to define a unique XML-name and/or -namespace for the type.

原因 XmlSerializer 对于层次结构中的所有类型都需要唯一的元素名称和/或名称空间,因为它被设计为能够通过 xsi:type 机制-如果XML名称和名称空间是相同的。您希望在序列化为XML时使根数据模型层次结构中的所有类型都无法区分,这与 XmlSerializer 的设计意图相冲突。

The reason XmlSerializer requires unique element names and/or namespaces for all types in the hierarchy is that it is designed to be able to successfully serialize type information via the xsi:type mechanism - which becomes impossible if the XML names & namespaces are identical. You wish to make all the types in your root data model hierarchy be indistinguishable when serialized to XML which conflicts with this design intent of XmlSerializer.

相反,序列化时,您可以使用XmlSerializer library / f1wczcys.aspx rel = nofollow noreferrer> XmlSerializer(Type,XmlRootAttribute) 构造函数,用于指定共享的根元素名称和命名空间根模型层次结构中的所有对象。然后,在反序列化时,可以使用文件中实际遇到的根元素名称和名称空间来构造 XmlSerializer 。可以使用以下扩展方法:

Instead, when serializing, you can construct your XmlSerializer with the XmlSerializer(Type, XmlRootAttribute) constructor to specify a shared root element name and namespace to be used for all objects in your root model hierarchy. Then when deserializing you can construct an XmlSerializer using the root element name and namespace actually encountered in the file. The following extension methods do the job:

public static partial class XmlSerializationHelper
{
    public static T LoadFromXmlAsType<T>(this string xmlString)
    {
        return new StringReader(xmlString).LoadFromXmlAsType<T>();
    }

    public static T LoadFromXmlAsType<T>(this TextReader textReader)
    {
        using (var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { CloseInput = false }))
            return xmlReader.LoadFromXmlAsType<T>();
    }

    public static T LoadFromXmlAsType<T>(this XmlReader xmlReader)
    {
        while (xmlReader.NodeType != XmlNodeType.Element)
            if (!xmlReader.Read())
                throw new XmlException("No root element");
        var serializer = XmlSerializerFactory.Create(typeof(T), xmlReader.LocalName, xmlReader.NamespaceURI);
        return (T)serializer.Deserialize(xmlReader);
    }

    public static string SaveToXmlAsType<T>(this T obj, string localName, string namespaceURI)
    {
        var sb = new StringBuilder();
        using (var writer = new StringWriter(sb))
            obj.SaveToXmlAsType(writer, localName, namespaceURI);
        return sb.ToString();
    }

    public static void SaveToXmlAsType<T>(this T obj, TextWriter textWriter, string localName, string namespaceURI)
    {
        using (var xmlWriter = XmlWriter.Create(textWriter, new XmlWriterSettings { CloseOutput = false, Indent = true }))
            obj.SaveToXmlAsType(xmlWriter, localName, namespaceURI);
    }

    public static void SaveToXmlAsType<T>(this T obj, XmlWriter xmlWriter, string localName, string namespaceURI)
    {
        var serializer = XmlSerializerFactory.Create(obj.GetType(), localName, namespaceURI);
        serializer.Serialize(xmlWriter, obj);
    }
}

public static class XmlSerializerFactory
{
    // To avoid a memory leak the serializer must be cached.
    // https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
    // This factory taken from 
    // https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648

    readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
    readonly static object padlock;

    static XmlSerializerFactory()
    {
        padlock = new object();
        cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
    }

    public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
    {
        if (serializedType == null)
            throw new ArgumentNullException();
        if (rootName == null && rootNamespace == null)
            return new XmlSerializer(serializedType);
        lock (padlock)
        {
            XmlSerializer serializer;
            var key = Tuple.Create(serializedType, rootName, rootNamespace);
            if (!cache.TryGetValue(key, out serializer))
                cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
            return serializer;
        }
    }
}

然后,如果您的类型层次为看起来像这样:

Then, if your type hierarchy looks something like this:

public class Project
{
    // Name for your root element.  Replace as desired.
    public const string RootElementName = "Project";
    // Namespace for your project.  Replace as required.
    public const string RootElementNamespaceURI = "https://stackoverflow.com/questions/49977144";

    public string BaseProperty { get; set; }
}

public class ProjectCustomerA : Project
{
    public string CustomerProperty { get; set; }

    public string ProjectCustomerAProperty { get; set; }
}

public class ProjectCustomerB : Project
{
    public string CustomerProperty { get; set; }

    public string ProjectCustomerBProperty { get; set; }
}

您可以序列化 ProjectCustomerA的实例并将其反序列化为 ProjectCustomerB 的实例,如下所示:

You can serialize an instance of ProjectCustomerA and deserialize it as an instance of ProjectCustomerB as follows:

var roota = new ProjectCustomerA
{
    BaseProperty = "base property value",
    CustomerProperty = "shared property value",
    ProjectCustomerAProperty = "project A value",
};
var xmla = roota.SaveToXmlAsType(Project.RootElementName, Project.RootElementNamespaceURI);

var rootb = xmla.LoadFromXmlAsType<ProjectCustomerB>();
var xmlb = rootb.SaveToXmlAsType(Project.RootElementName, Project.RootElementNamespaceURI);

// Assert that the shared BaseProperty was deserialized successfully.
Assert.IsTrue(roota.BaseProperty == rootb.BaseProperty);
// Assert that the same-named CustomerProperty was ported over properly.
Assert.IsTrue(roota.CustomerProperty == rootb.CustomerProperty);

注意:


  • 我选择将共享的XML元素名称和名称空间作为常量放入基本类型 Project

使用覆盖的根元素名称或名称空间构造 XmlSerializer 时,它必须为已缓存以免发生内存泄漏

When constructing an XmlSerializer with an override root element name or namespace, it must be cached to avoid a memory leak.

所有这些都被说成是不可能的。确定给定的XML文件是否包含类型为 ProjectCustomerA ProjectCustomerB 的对象似乎是危险的,不灵活的设计。我鼓励您重新考虑这种设计是否合适。例如,您可以使用其默认的唯一元素名称和名称空间来序列化它们,并仍然使用上述方法 LoadFromXmlAsType< T>(T)> code>将其反序列化为任何所需的类型。使用文件中的实际名称和名称空间生成 XmlSerializer

All this being said, making it be impossible to determine whether a given XML file contains an object of type ProjectCustomerA or ProjectCustomerB seems like a dangerously inflexible design going forward. I'd encourage you to rethink whether this design is appropriate. For instance, you could instead serialize them with their default, unique element names and namespaces, and still deserialize to any desired type using the methods LoadFromXmlAsType<T>() above, which generate an XmlSerializer using the actual name and namespace found in the file.

方法<$ c如果存在 LoadFromXmlAsType< T>()可能不起作用 nofollow noreferrer>根元素上的 xsi:type 属性。如果要忽略(或处理) xsi:type 属性,则可能需要进一步的工作。

The methods LoadFromXmlAsType<T>() may not work if there is an xsi:type attribute on the root element. If you want to ignore (or process) the xsi:type attribute then further work may be required.

示例工作 .Net提琴

这篇关于将1个XML的c#XML反序列化到不同的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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