将1个XML反序列化到不同类的c#XML

将1个XML反序列化到不同类的c#XML,c#,xml,deserialization,xmlserializer,C#,Xml,Deserialization,Xmlserializer,我想有效地管理多个客户标准 如果我打开(反序列化)一个XML,我想确定在反序列化过程中使用哪些类。选择另一个类基本上意味着从另一个角度(视图)查看XML 我现在所拥有的: 我有一个类项目,它有一些属性和方法。 我能够将motor的实例序列化为XML,这很好。 反序列化也可以很好地工作 现在,我创建了一个新类ProjectCustomerA,它派生自基类Project。我覆盖了ProjectCustomerA上的一些方法,将来可能会添加一些 类Project和ProjectCustomerA共享相

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

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

我现在所拥有的: 我有一个类
项目
,它有一些属性和方法。 我能够将motor的实例序列化为XML,这很好。 反序列化也可以很好地工作

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

Project
ProjectCustomerA
共享相同的
XmlType
[Serializable,XmlType(“项目”)]

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

下面是我收到的信息(最初是荷兰语,我翻译过):

System.InvalidOperationException HResult=0x80131509。。。内部的 异常1:无效操作异常:类型C4M_Data.C4M_项目 和C4M_Data_customer.C4M_Project_customer都使用XML类型名, 项目,来自命名空间。使用XML属性定义唯一的 类型的XML名称和/或命名空间

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


我目前的想法是,不同的类型(如果需要,所有的基类都相同)应该产生一个具有相同根元素和名称空间的XML。XML的外观应该始终相同。然后,我需要控制/强制
XmlSerializer
反序列化为我想要的类型,而不管根元素名称和命名空间如何。这可能吗?

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

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

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

相反,序列化时,可以使用构造函数构造
XmlSerializer
,以指定用于根模型层次结构中所有对象的共享根元素名称和命名空间。然后,在反序列化时,您可以使用文件中实际遇到的根元素名称和命名空间构造一个
XmlSerializer
。以下扩展方法可以完成此工作:

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;
        }
    }
}
您可以序列化
ProjectCustomerA
的实例,并将其反序列化为
ProjectCustomerB
的实例,如下所示:

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);
var roota=newprojectcustomera
{
BaseProperty=“基本属性值”,
CustomerProperty=“共享属性值”,
ProjectCustomerProperty=“项目A值”,
};
var xmla=roota.SaveToXmlAsType(Project.RootElementName,Project.RootElementNamespaceURI);
var rootb=xmla.LoadFromXmlAsType();
var xmlb=rootb.SaveToXmlAsType(Project.RootElementName,Project.RootElementNamespaceURI);
//断言已成功反序列化shared BaseProperty。
Assert.IsTrue(roota.BaseProperty==rootb.BaseProperty);
//断言相同的命名CustomerProperty已正确移植。
Assert.IsTrue(roota.CustomerProperty==rootb.CustomerProperty);
注:

  • 我选择将共享XML元素名称和命名空间作为常量放在基本类型
    项目中

  • 当构造具有覆盖根元素名称或命名空间的
    XmlSerializer
    时,它必须是

  • 综上所述,不可能确定给定的XML文件是否包含类型为
    ProjectCustomerA
    ProjectCustomerB
    的对象似乎是一种危险的僵化设计。我鼓励你重新思考这个设计是否合适。例如,您可以使用默认的、唯一的元素名称和名称空间对它们进行序列化,并且仍然可以使用上面的方法
    LoadFromXmlAsType()
    反序列化为任何所需的类型,这些方法使用文件中找到的实际名称和名称空间生成
    XmlSerializer

  • 如果根元素上有属性,则方法
    LoadFromXmlAsType()
    可能不起作用。如果要忽略(或处理)xsi:type属性,则可能需要进一步的工作


示例工作。

我认为您混淆了类名称,或者没有详细说明所有类(项目、电机),是吗?您试图反序列化的文件是来自您的代码还是来自其他源?如果是不同的源代码,请将C#代码与其他源代码进行比较,找出差异。通常情况下,您可以序列化为xml,但无法反序列化。要解决此问题,您必须在要解决问题的类中的类/属性上方添加属性定义。该错误表示您有两个同名的属性/类。可能是c#中的重载。正确,我混淆了类名。电机也应该是一个项目。是的,XML来自我的代码,同样的解决方案。我将项目及其所有内容以XML形式存储在SQL中-database@jdweng:您是对的,这两个类可以具有相同的XmlType名称。我希望这样,因为如果我
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);