2011-01-26 63 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); 
相關問題