2011-01-26 9 views
19

सी # में, मैं किसी ऑब्जेक्ट को deserialize करने के लिए XmlSerializer का उपयोग कैसे कर सकता हूं जो बेस क्लास या किसी भी व्युत्पन्न कक्षाओं में से किसी भी प्रकार को पहले बिना जाने?मैं किसी ऑब्जेक्ट को deserialize करने के लिए XmlSerializer का उपयोग कैसे करूं जो आधार या व्युत्पन्न कक्षा के पहले बिना किसी प्रकार के जानने के हो सकता है?

मेरे सभी व्युत्पन्न वर्ग अतिरिक्त डेटा सदस्यों को जोड़ते हैं। मैंने एक साधारण जीयूआई बनाया है जो कक्षा वस्तुओं को क्रमबद्ध और deserialize कर सकते हैं। यह ऑब्जेक्ट्स को क्रमबद्ध करेगा क्योंकि जो भी विरासत वर्ग (या यहां तक ​​कि केवल बेस क्लास) उपयुक्त है, जिसके आधार पर उपयोगकर्ता पॉप्युलेट करना चुनता है।

मुझे क्रमबद्धरण के साथ कोई समस्या नहीं है; समस्या deserialization है। मैं संभवतः XmlSerializer कक्षा को जानने के बिना सही व्युत्पन्न कक्षा में डेटा को deserialize कैसे कर सकता हूं? मैं वर्तमान में XML फ़ाइल के पहले नोड को पढ़ने के लिए XmlReader बना रहा हूं और इससे कक्षा निर्धारित करता हूं, और ऐसा लगता है कि यह मेरे उद्देश्यों के लिए काम करता है, लेकिन यह एक बेहद सुरुचिपूर्ण समाधान की तरह लगता है।

मैंने नीचे कुछ नमूना कोड पोस्ट किया है। कोई सुझाव?

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

बहुत शांत है, धन्यवाद! अच्छे उदाहरण के लिए –

3

आप निर्माता XmlSerializer(Type type, Type[] extraTypes) उपयोग करने के लिए एक serializer है कि सभी शामिल प्रकार के साथ काम करता है बनाने की कोशिश कर सकते हैं।

4

आप XmlSerializer आप के बजाय KnownType विशेषता के साथ DataContractSerializer उपयोग कर सकते हैं का उपयोग कर पर सेट नहीं कर रहे हैं।

आपको बस इतना करना है कि प्रत्येक उप वर्ग के लिए पेरेंट क्लास में KnownType विशेषता जोड़ें और DataContractSerializer बाकी करेगी।

DataContractSerializer xml को क्रमबद्ध करते समय टाइप जानकारी जोड़ देगा और उस प्रकार की जानकारी का उपयोग करेगा जब सही प्रकार बनाने के लिए deserializing।

उदाहरण के लिए निम्नलिखित कोड:

[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 के लिए एक्सएमएल ध्यान देंगे निहित अतिरिक्त प्रकार की जानकारी जो बनाने के लिए DataContractSerializer.ReadObject अनुमति सही प्रकार

+0

+1। – Beyers

4

मैंने हाल ही में बेस क्लास टी के लिए इस जेनेरिक सीरियलाइज़र \ deserializer लिखा है और टी के किसी भी व्युत्पन्न वर्ग अब तक काम करने लगता है।

प्रकार [] सरणी सभी व्युत्पन्न प्रकारों टी और टी को स्टोर करता है। deserializer उनमें से प्रत्येक की कोशिश करता है, और जब इसे सही मिला तो लौटाता है।

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

2

आप XmlInclude

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

आप प्रकार जोड़ना चाहते हैं अन्यथा यदि जब serializing उपयोग कर सकते हैं:

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

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