XML सीरियललाइज़ेशन और इनहेरिटेड प्रकार


84

अपने पिछले प्रश्न के बाद मैं XML को अनुक्रमित करने के लिए अपने ऑब्जेक्ट मॉडल को प्राप्त करने पर काम कर रहा हूं। लेकिन मैं अब एक समस्या में चला गया (आश्चर्य चकित!)।

मेरे पास समस्या यह है कि मेरे पास एक संग्रह है, जो एक अमूर्त बेस क्लास प्रकार का है, जो कंक्रीट व्युत्पन्न प्रकारों से आबाद है।

मुझे लगा कि इसमें शामिल सभी वर्गों के लिए XML विशेषताओं को जोड़ना ठीक होगा और सब कुछ आड़ू होगा। अफसोस की बात है, ऐसा नहीं है!

इसलिए मैंने Google पर कुछ खुदाई की है और मुझे अब समझ में आया कि यह काम क्यों नहीं कर रहा है। कि में वास्तव में क्रमानुसार करने के क्रम में कुछ चालाक प्रतिबिंब कर वस्तुओं के लिए / एक्सएमएल से, और उसके बाद से सार प्रकार के आधार पर, यह समझ नहीं क्या नरक यह करने के लिए बात कर रहा है । ठीक।XmlSerializer

मैं कोडप्रोजेक्ट पर इस पृष्ठ पर आया था , जो ऐसा दिखता है कि यह अच्छी तरह से बहुत मदद कर सकता है (अभी तक पूरी तरह से पढ़ने / उपभोग करने के लिए), लेकिन मुझे लगा कि मैं इस समस्या को स्टैकऑवरफ्लो तालिका में भी लाना चाहूंगा, यह देखने के लिए कि क्या आपके पास कोई नीट है इसे प्राप्त करने के लिए हैक्स / ट्रिक्स संभव और सबसे तेज / हल्के तरीके से चलने के लिए।

मुझे यह भी जोड़ना चाहिए कि मैं मार्ग से नीचे नहीं जाना चाहता XmlInclude। इसके साथ बस बहुत अधिक युग्मन है, और सिस्टम का यह क्षेत्र भारी विकास के अधीन है, इसलिए यह एक वास्तविक रखरखाव सिरदर्द होगा!


1
उन वर्गों से निकाले गए कुछ प्रासंगिक कोड स्निपेट्स देखना उपयोगी होगा जिन्हें आप क्रमबद्ध करने की कोशिश कर रहे हैं।
रेक्स एम

मेट: मैं फिर से खोल दिया गया क्योंकि मुझे लगता है कि अन्य लोग इसे उपयोगी पा सकते हैं, लेकिन अगर आप असहमत हैं तो बंद करने के लिए स्वतंत्र महसूस करें
JamesSugrue

इस से थोड़ा भ्रमित, क्योंकि इस धागे पर इतने लंबे समय से कुछ भी नहीं है?
रॉब कूपर

इसका जवाब है: stackoverflow.com/questions/6737666/…
Odys

जवाबों:


54

समस्या सुलझ गयी!

ठीक है, इसलिए मैं अंत में वहां पहुंच गया ( यहाँ से बहुत मदद के साथ ( !)

इसलिए संक्षिप्त करें:

लक्ष्य:

  • मैं मुख्य सिरदर्द के कारण XmlInclude मार्ग से नीचे नहीं जाना चाहता था ।
  • एक बार एक समाधान मिल गया था, मैं चाहता था कि यह अन्य अनुप्रयोगों में लागू करने के लिए जल्दी हो।
  • सार प्रकार के संग्रह का उपयोग किया जा सकता है, साथ ही साथ व्यक्तिगत सार गुण भी।
  • मैं वास्तव में ठोस वर्गों में "विशेष" चीजें करने से परेशान नहीं होना चाहता था।

नोट करने के लिए पहचाने गए मुद्दे / अंक:

  • XmlSerializer कुछ बहुत अच्छा परावर्तन करता है, लेकिन यह बहुत सीमित है जब यह अमूर्त प्रकारों की बात आती है (अर्थात यह केवल अमूर्त प्रकार के उदाहरणों के साथ काम करेगा, न कि उपवर्ग)।
  • Xml विशेषता डेकोरेटर्स यह परिभाषित करते हैं कि XmlSerializer कैसे अपने गुणों का पता लगाता है। भौतिक प्रकार भी निर्दिष्ट किया जा सकता है, लेकिन यह वर्ग और धारावाहिक (अच्छे नहीं) के बीच एक तंग युग्मन बनाता है ।
  • हम अपने खुद के XmlSerializer को एक वर्ग बनाकर कार्यान्वित कर सकते हैं जो IXmlSerializable को लागू करता है ।

समाधान

मैंने एक जेनेरिक क्लास बनाया, जिसमें आप जेनेरिक प्रकार को उस अमूर्त प्रकार के रूप में निर्दिष्ट करते हैं जिसके साथ आप काम कर रहे होंगे। यह वर्ग को अमूर्त प्रकार और ठोस प्रकार के बीच "अनुवाद" करने की क्षमता प्रदान करता है क्योंकि हम कास्टिंग को हार्ड-कोड कर सकते हैं (यानी हम एक्सएमएलसियर की तुलना में अधिक जानकारी प्राप्त कर सकते हैं)।

मैंने तब IXmlSerializable इंटरफ़ेस लागू किया , यह बहुत ही सीधे आगे है, लेकिन जब क्रमबद्ध करने के लिए हमें यह सुनिश्चित करने की आवश्यकता है कि हम XML को ठोस वर्ग का प्रकार लिखें, तो हम इसे डी-सीरियलिंग करते समय वापस ला सकते हैं। यह भी ध्यान रखना महत्वपूर्ण है कि यह पूरी तरह से योग्य होना चाहिए क्योंकि असेंबली दोनों वर्गों के अलग-अलग होने की संभावना है। बेशक थोड़ा प्रकार की जाँच और सामान है जो यहाँ होने की आवश्यकता है।

चूंकि XmlSerializer कास्ट नहीं कर सकता है, हमें ऐसा करने के लिए कोड प्रदान करने की आवश्यकता है, इसलिए निहित ऑपरेटर को फिर से अधिभारित किया गया है (मुझे कभी भी नहीं पता था कि आप ऐसा कर सकते हैं!)।

इस AbstractXmlSerializer के लिए कोड है:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace Utility.Xml
{
    public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
    {
        // Override the Implicit Conversions Since the XmlSerializer
        // Casts to/from the required types implicitly.
        public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
        {
            return o.Data;
        }

        public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
        {
            return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
        }

        private AbstractType _data;
        /// <summary>
        /// [Concrete] Data to be stored/is stored as XML.
        /// </summary>
        public AbstractType Data
        {
            get { return _data; }
            set { _data = value; }
        }

        /// <summary>
        /// **DO NOT USE** This is only added to enable XML Serialization.
        /// </summary>
        /// <remarks>DO NOT USE THIS CONSTRUCTOR</remarks>
        public AbstractXmlSerializer()
        {
            // Default Ctor (Required for Xml Serialization - DO NOT USE)
        }

        /// <summary>
        /// Initialises the Serializer to work with the given data.
        /// </summary>
        /// <param name="data">Concrete Object of the AbstractType Specified.</param>
        public AbstractXmlSerializer(AbstractType data)
        {
            _data = data;
        }

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null; // this is fine as schema is unknown.
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            // Cast the Data back from the Abstract Type.
            string typeAttrib = reader.GetAttribute("type");

            // Ensure the Type was Specified
            if (typeAttrib == null)
                throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because no 'type' attribute was specified in the XML.");

            Type type = Type.GetType(typeAttrib);

            // Check the Type is Found.
            if (type == null)
                throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because the type specified in the XML was not found.");

            // Check the Type is a Subclass of the AbstractType.
            if (!type.IsSubclassOf(typeof(AbstractType)))
                throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
                    "' because the Type specified in the XML differs ('" + type.Name + "').");

            // Read the Data, Deserializing based on the (now known) concrete type.
            reader.ReadStartElement();
            this.Data = (AbstractType)new
                XmlSerializer(type).Deserialize(reader);
            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            // Write the Type Name to the XML Element as an Attrib and Serialize
            Type type = _data.GetType();

            // BugFix: Assembly must be FQN since Types can/are external to current.
            writer.WriteAttributeString("type", type.AssemblyQualifiedName);
            new XmlSerializer(type).Serialize(writer, _data);
        }

        #endregion
    }
}

तो, वहाँ से, हम कैसे XmlSerializer को डिफ़ॉल्ट के बजाय हमारे धारावाहिक के साथ काम करने के लिए कहते हैं? हमें अपने प्रकार को Xml विशेषताओं प्रकार की संपत्ति में पास करना चाहिए, उदाहरण के लिए:

[XmlRoot("ClassWithAbstractCollection")]
public class ClassWithAbstractCollection
{
    private List<AbstractType> _list;
    [XmlArray("ListItems")]
    [XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))]
    public List<AbstractType> List
    {
        get { return _list; }
        set { _list = value; }
    }

    private AbstractType _prop;
    [XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))]
    public AbstractType MyProperty
    {
        get { return _prop; }
        set { _prop = value; }
    }

    public ClassWithAbstractCollection()
    {
        _list = new List<AbstractType>();
    }
}

यहां आप देख सकते हैं, हमारे पास एक संग्रह और एक एकल संपत्ति उजागर हो रही है, और हमें बस इतना करना है कि नाम टाइप पैरामीटर को Xml घोषणा में जोड़ दें , आसान! : डी

नोट: यदि आप इस कोड का उपयोग करते हैं, तो मैं वास्तव में एक चिल्लाओ की सराहना करूंगा। यह समुदाय में अधिक लोगों को ड्राइव करने में भी मदद करेगा :)

अब, लेकिन अनिश्चित के रूप में यहाँ क्या जवाब के साथ के बाद से वे सब उनके समर्थक है और चुनाव है। मैं उन लोगों को उखाड़ फेंकूंगा जो मुझे लगता है कि उपयोगी थे (जो उन लोगों के लिए कोई अपराध नहीं थे) और इस प्रतिनिधि को एक बार बंद करने के बाद मैं प्रतिनिधि हूं :)

दिलचस्प समस्या और हल करने के लिए अच्छा मज़ा! :)


मैं कुछ समय पहले खुद इस समस्या में भाग गया था। व्यक्तिगत रूप से, मैंने XmlSerializer को छोड़ना और IXmlSerializable इंटरफ़ेस का उपयोग करना सीधे समाप्त कर दिया, क्योंकि मेरे सभी वर्गों को इसे वैसे भी लागू करने की आवश्यकता थी। अन्यथा, समाधान काफी समान हैं। अच्छा लेखन हालांकि :)
थोरारिन

हम XML_ गुणों का उपयोग करते हैं जहां हम सूची को Arrays :)
Arcturus

2
क्योंकि क्लास को डायनेमिक रूप से इंस्टेंट करने के लिए एक पैरामीटर रहित कंस्ट्रक्टर की आवश्यकता होती है।
सिलास हैनसेन

1
नमस्कार! मैं काफी समय से इस तरह के समाधान की तलाश कर रहा हूं। मुझे लगता है कि यह शानदार है! हालाँकि, यह पता लगाने में सक्षम नहीं है कि इसका उपयोग कैसे करें, क्या आप एक उदाहरण देना चाहेंगे? क्या आप अपनी वस्तुओं से युक्त अपनी कक्षा या सूची को क्रमबद्ध कर रहे हैं?
डैनियल

1
अच्छा कोड है। ध्यान दें कि पैरामीटर रहित अवरोधक घोषित किया जा सकता है privateया protectedइसे लागू करने के लिए अन्य वर्गों के लिए उपलब्ध नहीं है।
13

9

देखने वाली एक बात यह है कि XmlSerialiser कंस्ट्रक्टर में आप एक प्रकार का सरणी पास कर सकते हैं जो कि serializer को हल करने में कठिनाई हो रही हो। मुझे वह काफी बार उपयोग करना पड़ा है जहाँ डेटास्ट्रक्चर के संग्रह या जटिल सेट को क्रमबद्ध करने की आवश्यकता होती है और वे प्रकार अलग-अलग विधानसभाओं आदि में रहते थे।

एक्स्ट्राटाइप्स परम के साथ XmlSerialiser कंस्ट्रक्टर

संपादित करें: मैं कहूंगा कि इस दृष्टिकोण से XmlInclude विशेषताओं आदि पर लाभ है कि आप रनवे पर अपने संभावित ठोस प्रकारों की एक सूची की खोज और संकलन का काम कर सकते हैं और उन्हें इसमें भर सकते हैं।


यह वही है जो मैं करने की कोशिश कर रहा हूं, लेकिन यह आसान नहीं है क्योंकि मैं सोच रहा था: stackoverflow.com/questions/3897818/…
Luca

यह एक बहुत पुरानी पोस्ट है, लेकिन इसे लागू करने वाले किसी भी व्यक्ति के लिए जैसे हमने किया, कृपया ध्यान दें कि XmlSerializer के एक्स्ट्राटाइपर के साथ निर्माता परम को कैश को इकट्ठा नहीं करता है जो इसे मक्खी पर उत्पन्न करता है। यह हमें उस स्मृति रिसाव को डिबग करने के हफ्तों की लागत देता है। इसलिए यदि आप स्वीकृत उत्तर के कोड के साथ अतिरिक्त प्रकारों का उपयोग करना चाहते हैं, तो धारावाहिक को कैश करें । इस व्यवहार को यहाँ प्रलेखित किया गया है: support.microsoft.com/en-us/kb/886385
जुलिएन लेओबेट

3

गंभीरता से, POCOs का एक एक्स्टेंसिबल फ्रेमवर्क XML को मज़बूती से कभी भी प्रसारित नहीं करेगा। मैं यह कहता हूं क्योंकि मैं गारंटी दे सकता हूं कि कोई व्यक्ति आपके साथ आएगा, आपकी कक्षा का विस्तार करेगा, और उसका पालन-पोषण करेगा।

आपको अपने ऑब्जेक्ट ग्राफ़ को क्रमबद्ध करने के लिए XAML का उपयोग करना चाहिए। इसे ऐसा करने के लिए डिज़ाइन किया गया है, जबकि XML क्रमांकन नहीं है।

Xaml serializer और deserializer एक समस्या के बिना जेनेरिक को संभालता है, बेस कक्षाओं और इंटरफेस के संग्रह के साथ-साथ (जब तक कि संग्रह खुद को लागू करते हैं IListया IDictionary)। कुछ कैविएट हैं, जैसे कि आपके रीड ओनली कलेक्शन प्रॉपर्टीज़ को DesignerSerializationAttributeमार्क करना , लेकिन इन कॉर्नर के मामलों को संभालने के लिए अपने कोड को रिजेक्ट करना उतना मुश्किल नहीं है।


लिंक मृत प्रतीत होता है
bkribbs

ओह अच्छा। मैं उस बिट की नकल करेंगे। विषय के बारे में बहुत से अन्य संसाधन।

2

इस पर बस एक त्वरित अद्यतन, मैं नहीं भूल गया हूँ!

बस कुछ और शोध कर रहे हैं, लगता है कि मैं एक विजेता हूं, बस कोड को हल करने की आवश्यकता है।

अब तक, मेरे पास निम्नलिखित हैं:

  • XmlSeralizer मूल रूप से एक वर्ग है कि कक्षाओं यह serializing है पर कुछ गंधा प्रतिबिंब करता है। यह उन गुणों को निर्धारित करता है जो टाइप के आधार पर क्रमबद्ध होते हैं ।
  • कारण समस्या होती है क्योंकि एक प्रकार मेल नहीं खाता हो रहा है जो यह उम्मीद कर रही है BaseType लेकिन वास्तव में प्राप्त करता है DerivedType .. आपको लगता है हो सकता है कि यह यह polymorphically का इलाज होता है, यदि ऐसा नहीं होता है, क्योंकि यह की एक पूरी अतिरिक्त भार शामिल होगा प्रतिबिंब और प्रकार-जाँच, जिसे इसे करने के लिए डिज़ाइन नहीं किया गया है।

यह व्यवहार धारावाहिक के लिए जाने-माने के रूप में कार्य करने के लिए एक प्रॉक्सी क्लास बनाकर (कोड लंबित) ओवरराइड करने में सक्षम प्रतीत होता है। यह मूल रूप से व्युत्पन्न वर्ग के प्रकार को निर्धारित करेगा और फिर इसे सामान्य रूप में अनुक्रमित करेगा। यह प्रॉक्सी क्लास तब एक्सएमएल को मुख्य धारावाहिक के लिए लाइन अप फीड करेगा।

इस जगह को देखो! ^ _ ^


2

यह निश्चित रूप से आपकी समस्या का समाधान है, लेकिन एक और समस्या है, जो कुछ हद तक "पोर्टेबल" XML प्रारूप का उपयोग करने के आपके इरादे को कम करती है। बुरी बात तब होती है जब आप अपने कार्यक्रम के अगले संस्करण में कक्षाएं बदलने का फैसला करते हैं और आपको क्रमबद्धता के दोनों स्वरूपों का समर्थन करने की आवश्यकता होती है - नया एक और पुराना (क्योंकि आपके ग्राहक अभी भी पुरानी पुरानी फ़ाइलों / डेटाबेस का उपयोग करते हैं, या वे से कनेक्ट होते हैं आपका सर्वर आपके उत्पाद के पुराने संस्करण का उपयोग कर रहा है)। लेकिन आप इस क्रम का उपयोग नहीं कर सकते हैं, क्योंकि आपने उपयोग किया है

type.AssemblyQualifiedName

जो दिखता है

TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

जिसमें आपकी विधानसभा विशेषताएँ और संस्करण शामिल हैं ...

अब यदि आप अपना असेंबली संस्करण बदलने की कोशिश करते हैं, या आप इस पर हस्ताक्षर करने का निर्णय लेते हैं, तो यह डिसेरिएलाइज़ेशन काम नहीं करने वाला है ...


1

मैंने इसके समान चीजें की हैं। मैं सामान्य रूप से यह सुनिश्चित करता हूं कि सभी XML क्रमांकन विशेषताएँ ठोस वर्ग पर हैं, और बस उस वर्ग के गुणों को आधार कक्षाओं (जहां आवश्यक हो) के माध्यम से जानकारी प्राप्त करने के लिए कहा जाता है जो कि धारावाहिककार द्वारा कॉल किए जाने पर डी / क्रमबद्ध हो जाएगा। उन गुणों। यह थोड़ा अधिक कोडिंग का काम है, लेकिन यह सही काम करने के लिए धारावाहिक को मजबूर करने के प्रयास की तुलना में बहुत बेहतर काम करता है।


1

इससे भी बेहतर, संकेतन का उपयोग करना:

[XmlRoot]
public class MyClass {
    public abstract class MyAbstract {} 
    public class MyInherited : MyAbstract {} 
    [XmlArray(), XmlArrayItem(typeof(MyInherited))] 
    public MyAbstract[] Items {get; set; } 
}

2
यह बहुत अच्छा है यदि आप अपनी कक्षाओं को जानते हैं, तो यह सबसे सुंदर लक्षण है। यदि आप बाहरी स्रोत से नई विरासत वाली कक्षाएं लोड करते हैं तो आप दुर्भाग्य से इसका उपयोग नहीं कर सकते।
व्लादिमीर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.