Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用XmlSerializer反序列化可能属于基类或派生类的对象,而不事先知道其类型?_C#_Xml Serialization_Derived Class_Xml Deserialization - Fatal编程技术网

C# 如何使用XmlSerializer反序列化可能属于基类或派生类的对象,而不事先知道其类型?

C# 如何使用XmlSerializer反序列化可能属于基类或派生类的对象,而不事先知道其类型?,c#,xml-serialization,derived-class,xml-deserialization,C#,Xml Serialization,Derived Class,Xml Deserialization,在C#中,如何使用XmlSerializer反序列化可能属于基类或多个派生类中的任何一个的对象,而不事先知道其类型 我的所有派生类都添加了额外的数据成员。我制作了一个简单的GUI,可以序列化和反序列化类对象。它将序列化对象,因为根据用户选择填充的字段,继承的类(甚至只是基类)是合适的 我对序列化没有任何问题;问题在于反序列化。在事先不知道类的情况下,如何让XmlSerializer将数据反序列化到正确的派生类?我目前创建了一个XmlReader来读取XML文件的第一个节点并从中确定类,它似乎适合

在C#中,如何使用
XmlSerializer
反序列化可能属于基类或多个派生类中的任何一个的对象,而不事先知道其类型

我的所有派生类都添加了额外的数据成员。我制作了一个简单的GUI,可以序列化和反序列化类对象。它将序列化对象,因为根据用户选择填充的字段,继承的类(甚至只是基类)是合适的

我对序列化没有任何问题;问题在于反序列化。在事先不知道类的情况下,如何让
XmlSerializer
将数据反序列化到正确的派生类?我目前创建了一个
XmlReader
来读取XML文件的第一个节点并从中确定类,它似乎适合我的目的,但它似乎是一个非常不雅观的解决方案

我在下面发布了一些示例代码。有什么建议吗

BaseType objectOfConcern = new BaseType();
XmlSerializer xserializer;
XmlTextReader xtextreader = new XmlTextReader(DEFAULT_FILENAME);

do { xtextreader.Read(); } while (xtextreader.NodeType != XmlNodeType.Element);

string objectType = xtextreader.Name;
xtextreader.Close();

FileStream fstream = new FileStream(DEFAULT_FILENAME, FileMode.Open);

switch (objectType)
    {
case "type1":
    xserializer = new XmlSerializer(typeof(DerivedType));

    objectOfConcern = (DerivedType)xserializer.Deserialize(fstream);

    //Load fields specific to that derived type here
    whatever = (objectOfConcern as DerivedType).NoOfstreamubordinates.ToString();

    case "xxx_1":
        //code here

    case "xxx_2":
        //code here

    case "xxx_n":
        //code here

        //and so forth

    case "BaseType":
    xserializer = new XmlSerializer(typeof(BaseType));
    AssignEventHandler(xserializer);
    objectOfConcern = (BaseType)xserializer.Deserialize(fstream);
}

//Assign all deserialized values from base class common to all derived classes here

//Close the FileStream
fstream.Close();

您可以尝试使用构造函数创建一个可与所有相关类型一起工作的序列化程序。

您是否有一些根类/标记包含该派生类型?如果是,您可以使用将标记名称映射到类型:

public class RootElementClass
{
    [XmlElement(ElementName = "Derived1", Type = typeof(Derived1BaseType))]
    [XmlElement(ElementName = "Derived2", Type = typeof(Derived2BaseType))]
    [XmlElement(ElementName = "Derived3", Type = typeof(Derived3BaseType))]
    public BaseType MyProperty { get; set; }
}

public class BaseType { }
public class Derived1BaseType : BaseType { }
public class Derived2BaseType : BaseType { }
public class Derived3BaseType : BaseType { }

如果未设置使用
XmlSerializer
,则可以将与属性一起使用

您所需要做的就是为每个子类的父类添加一个
KnownType
属性,
DataContractSerializer
将完成其余工作

DataContractSerializer
将在序列化为xml时添加类型信息,并在反序列化时使用该类型信息创建正确的类型

例如,以下代码:

[KnownType( typeof( C2 ) )]
[KnownType( typeof( C3 ) )]
public class C1 {public string P1 {get;set;}}
public class C2 :C1 {public string P2 {get;set;}}
public class C3 :C1 {public string P3 {get;set;}}

class Program
{
  static void Main(string[] args)
  {
    var c1 = new C1{ P1="c1"};
    var c2 = new C2{ P1="c1", P2="c2"};
    var c3 = new C3{ P1="c1", P3="c3"};

    var s = new DataContractSerializer( typeof( C1 ) );
    Test( c1, s );
    Test( c2, s );
    Test( c3, s );
  }

  static void Test( C1 objectToSerialize, DataContractSerializer serializer )
  {
    using ( var stream = new MemoryStream() )
    {
      serializer.WriteObject( stream, objectToSerialize );
      stream.WriteTo( Console.OpenStandardOutput() );
      stream.Position = 0;
      var deserialized = serializer.ReadObject( stream );
      Console.WriteLine( Environment.NewLine + "Deserialized Type: " + deserialized.GetType().FullName );              
    }
  }
}
将输出:

<C1 xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<P1>c1</P1></C1>

Deserialized Type: ConsoleApplication1.C1

<C1 i:type="C2" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<P1>c1</P1><P2>c2</P2></C1>

Deserialized Type: ConsoleApplication1.C2

<C1 i:type="C3" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<P1>c1</P1><P3>c3</P3></C1>

Deserialized Type: ConsoleApplication1.C3

c1
反序列化类型:ConsoleApplication1.C1
c1c2
反序列化类型:ConsoleApplication1.C2
c1c3
反序列化类型:ConsoleApplication1.C3

在输出中,您会注意到c2和c3的xml包含额外的类型信息,这允许
DataContractSerializer.ReadObject
创建正确的类型。

我最近为基类T和T的任何派生类编写了这个通用序列化器\反序列化器。 到目前为止似乎有效

Type[]数组存储T和T本身的所有派生类型。 反序列化程序将尝试其中的每一项,并在找到正确的项时返回

/// <summary>
/// A generic serializer\deserializer
/// </summary>
/// <typeparam name="T"></typeparam>
public static class Serializer<T>
{
    /// <summary>
    /// serialize an instance to xml
    /// </summary>
    /// <param name="instance"> instance to serialize </param>
    /// <returns> instance as xml string </returns>
    public static string Serialize(T instance)
    {
        StringBuilder sb = new StringBuilder();
        XmlWriterSettings settings = new XmlWriterSettings();

        using (XmlWriter writer = XmlWriter.Create(sb, settings))
        {
            XmlSerializer serializer = new XmlSerializer(instance.GetType());
            serializer.Serialize(writer, instance);
        }

        return sb.ToString();
    }

    /// <summary>
    /// deserialize an xml into an instance
    /// </summary>
    /// <param name="xml"> xml string </param>
    /// <returns> instance </returns>
    public static T Deserialize(string xml)
    {
        using (XmlReader reader = XmlReader.Create(new StringReader(xml)))
        {
            foreach (Type t in types)
            {
                XmlSerializer serializer = new XmlSerializer(t);
                if (serializer.CanDeserialize(reader))
                    return (T)serializer.Deserialize(reader);
            }
        }

        return default(T);
    }

    /// <summary>
    /// store all derived types of T:
    /// is used in deserialization
    /// </summary>
    private static Type[] types = AppDomain.CurrentDomain.GetAssemblies()
                                        .SelectMany(s => s.GetTypes())
                                        .Where(t => typeof(T).IsAssignableFrom(t)
                                            && t.IsClass
                                            && !t.IsGenericType)
                                            .ToArray();
}
//
///通用序列化程序\反序列化程序
/// 
/// 
公共静态类序列化程序
{
/// 
///将实例序列化为xml
/// 
///要序列化的实例
///实例作为xml字符串
公共静态字符串序列化(T实例)
{
StringBuilder sb=新的StringBuilder();
XmlWriterSettings=新的XmlWriterSettings();
使用(XmlWriter=XmlWriter.Create(sb,设置))
{
XmlSerializer serializer=新的XmlSerializer(instance.GetType());
serializer.Serialize(编写器、实例);
}
使某人返回字符串();
}
/// 
///将xml反序列化为实例
/// 
///xml字符串
///实例
公共静态T反序列化(字符串xml)
{
使用(XmlReader=XmlReader.Create(新的StringReader(xml)))
{
foreach(类型中的类型t)
{
XmlSerializer serializer=新的XmlSerializer(t);
if(序列化器.CanDeserialize(读卡器))
返回(T)序列化程序。反序列化(读取器);
}
}
返回默认值(T);
}
/// 
///存储T的所有派生类型:
///用于反序列化
/// 
私有静态类型[]类型=AppDomain.CurrentDomain.GetAssemblys()
.SelectMany(s=>s.GetTypes())
其中(t=>typeof(t).IsAssignableFrom(t)
&&t.I类
&&!t.IsGenericType)
.ToArray();
}
您可以使用XmlInclude

[XmlInclude(typeof(MyClass))]
public abstract class MyBaseClass
{
   //...
}
否则,如果要在序列化时添加类型:

Type[] types = new Type[]{ typeof(MyClass) }

XmlSerializer serializer = new XmlSerializer(typeof(MyBaseClass), types);

正是我想要的,谢谢!伟大的你救了我一天!