2011-01-26 23 views
19

C# 'da, bir temel sınıf veya önceden türünü bilmeden türetilmiş birkaç sınıftan herhangi biri olabilecek bir nesneyi serileştirmek için XmlSerializer'u nasıl kullanırım?Bir XmlSerializer'ı, türü önceden bilmeden bir temel veya türetilmiş bir sınıf olabilecek bir nesneyi serileştirmek için nasıl kullanırım?

Tüm türetilmiş sınıflarım ek veri üyeleri ekliyor. Sınıf nesnelerini serileştirip seri hale getirebilen basit bir GUI yaptım. Nesneleri, kalıtılan sınıfın (veya sadece temel sınıfın), kullanıcının hangi alanları doldurmak için seçtiğine göre uygun şekilde serileştirir.

Serileştirmeyle ilgili sorunum yok; Sorun, serileştirme. Sınıfı önceden bilmeden XmlSerializer serisini doğru türetilmiş sınıfa nasıl sahip olabilirim? Şu anda XML dosyasının ilk düğümünü okumak ve onu sınıftan belirlemek için bir XmlReader oluşturuyorum ve amaçlarım için çalışıyor gibi görünüyor, ama son derece temkinli bir çözüm gibi görünüyor.

Aşağıda bazı örnek kodlar yayınladım. Baska öneri?

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(); 

cevap

17

Bu tür türlerini içeren bazı kök sınıf/etiketler var mı? evet, haritaya XmlElementAttribute kullanabilirsek etiket adı yazın:

Eğer XmlSerializer yerine KnownType özniteliği ile DataContractSerializer kullanabilirsiniz kullanarak üzerine set değilseniz
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

Çok serin, teşekkürler! İyi örnek için –

4

.

Yapmanız gereken tek şey, her alt sınıfın üst sınıfına bir KnownType özniteliği eklemektir ve DataContractSerializer geri kalanını yapacaktır.

DataContractSerializer, xml'ye serileştirirken tür bilgisi ekler ve doğru tür oluşturmak için seri hale getirme sırasında bu tür bilgileri kullanır. Örneğin

Aşağıdaki kod:

[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);    
    } 
    } 
} 

Will çıkışı: Eğer c2 ve c3 için xml fark edeceksiniz

<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 

çıktısında oluşturmak için DataContractSerializer.ReadObject izin ekstra türü bilgilerini içeriyordu Doğru tip

+0

+1. – Beyers

4

Geçtiğimiz günlerde bu temel seri hale getirici \ serilaştırıcıyı temel sınıf T için yazdım ve şu ana kadar T. Seems'in türetilmiş sınıfları.

Tür [] dizisi, T ve T'nin türetilmiş tüm türlerini depolar. Desperizatör her birini dener ve doğru olanı bulduğunda döner.

/// <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

Tam olarak aradığım şey, Teşekkürler! – whywhywhy

2

Eğer XmlInclude

[XmlInclude(typeof(MyClass))] 
public abstract class MyBaseClass 
{ 
    //... 
} 
seri yaparken türleri eklemek istediğiniz aksi takdirde

kullanabilirsiniz:

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

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