2011-01-26 62 views
19

在C#,如何使用XmlSerializer反序列化的对象可以是基类或任何几种派生类的事先不知道类型?如何在事先不知道类型的情况下使用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(); 

回答

17

你有没有一些根类/标签,它包含了派生类型?如果是的话,你可以用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 { } 
+0

非常酷,非常感谢! – 2015-05-26 19:21:32

4

如果你不使用时将XmlSerializer可以使用DataContractSerializerKnownType属性,而不是设置。

您只需要为每个子类添加一个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 

在输出你会发现对C2和C3的XML载有允许DataContractSerializer.ReadObject创建额外的类型信息正确的类型。

+0

+1为例。 – Beyers 2011-07-04 23:34:01

4

我最近为基类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(); 
} 
+0

正是我在找的,谢谢! – whywhywhy 2016-01-13 12:41:44

2

可以使用XmlInclude

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

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

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

XmlSerializer serializer = new XmlSerializer(typeof(MyBaseClass), types); 
相关问题