如何在不事先知道类型的情况下使用XmlSerializer反序列化可能是基类或派生类的对象?

在C#中,如何使用XmlSerializer反序列化可能是基类的对象,或者在不事先知道类型的情况下反序列化任何几个派生类?

我的所有派生类都添加了其他数据成员。 我做了一个简单的GUI,可以序列化和反序列化类对象。 它将序列化对象,因为任何inheritance的类(甚至只是基类)都适合于用户选择填充的字段。

我对序列化没有任何问题; 问题是反序列化。 如何在不事先知道类的情况下将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(); 

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

 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 { } 

我最近为基类T和T的任何派生类编写了这个通用的serializer \ deserializer。似乎到目前为止工作。

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

 ///  /// A generic serializer\deserializer ///  ///  public static class Serializer { ///  /// serialize an instance to xml ///  ///  instance to serialize  ///  instance as xml string  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(); } ///  /// deserialize an xml into an instance ///  ///  xml string  ///  instance  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); } ///  /// store all derived types of T: /// is used in deserialization ///  private static Type[] types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) .Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass && !t.IsGenericType) .ToArray(); } 

如果您未设置使用XmlSerializer ,则可以使用带有KnownType属性的DataContractSerializer

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

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

例如,以下代码:

 [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 Deserialized Type: ConsoleApplication1.C1  c1c2 Deserialized Type: ConsoleApplication1.C2  c1c3 Deserialized Type: ConsoleApplication1.C3 

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

您可以尝试使用构造函数XmlSerializer(Type type,Type [] extraTypes)来创建一个适用于所有相关类型的序列化程序。

你可以使用XmlInclude

 [XmlInclude(typeof(MyClass))] public abstract class MyBaseClass { //... } 

否则,如果要在序列化时添加类型:

 Type[] types = new Type[]{ typeof(MyClass) } XmlSerializer serializer = new XmlSerializer(typeof(MyBaseClass), types);