IXmlSerializable लागू करने का उचित तरीका?


153

एक बार जब कोई प्रोग्रामर इसे लागू करने का निर्णय लेता है IXmlSerializable, तो इसे लागू करने के लिए कौन से नियम और सर्वोत्तम प्रथाएं हैं? मैंने सुना है कि GetSchema()लौटना चाहिए nullऔर ReadXmlलौटने से पहले अगले तत्व की ओर बढ़ना चाहिए। क्या ये सच है? और किस बारे में WriteXml- क्या उसे वस्तु के लिए एक मूल तत्व लिखना चाहिए या यह मान लिया जाए कि जड़ पहले से ही लिखी गई है? बाल वस्तुओं का इलाज और लेखन कैसे किया जाना चाहिए?

अब मेरे पास जो है उसका एक नमूना है। मुझे अच्छी प्रतिक्रियाएं मिलने के साथ मैं इसे अपडेट कर दूंगा।

public class MyCalendar : IXmlSerializable
{
    private string _name;
    private bool _enabled;
    private Color _color;
    private List<MyEvent> _events = new List<MyEvent>();


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCalendar")
        {
            _name    = reader["Name"];
            _enabled = Boolean.Parse(reader["Enabled"]);
            _color   = Color.FromArgb(Int32.Parse(reader["Color"]));

            if (reader.ReadToDescendant("MyEvent"))
            {
                while (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
                {
                    MyEvent evt = new MyEvent();
                    evt.ReadXml(reader);
                    _events.Add(evt);
                }
            }
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Name",    _name);
        writer.WriteAttributeString("Enabled", _enabled.ToString());
        writer.WriteAttributeString("Color",   _color.ToArgb().ToString());

        foreach (MyEvent evt in _events)
        {
            writer.WriteStartElement("MyEvent");
            evt.WriteXml(writer);
            writer.WriteEndElement();
        }
    }
}

public class MyEvent : IXmlSerializable
{
    private string _title;
    private DateTime _start;
    private DateTime _stop;


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
        {
            _title = reader["Title"];
            _start = DateTime.FromBinary(Int64.Parse(reader["Start"]));
            _stop  = DateTime.FromBinary(Int64.Parse(reader["Stop"]));
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Title", _title);
        writer.WriteAttributeString("Start", _start.ToBinary().ToString());
        writer.WriteAttributeString("Stop",  _stop.ToBinary().ToString());
    }
}

नमूना XML

<MyCalendar Name="Master Plan" Enabled="True" Color="-14069085">
    <MyEvent Title="Write Code" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="???" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="Profit!" Start="-8589247048854775808" Stop="-8589246976854775808" />
</MyCalendar>

3
क्या आप इस प्रश्न में एक xml नमूना जोड़ सकते हैं? यह कोड के साथ पढ़ने के लिए सरल बना देगा। धन्यवाद!
रोरी

उस मामले से निपटने के बारे में जहां आपके XML में अंतिम ईवेंट के बाद XML टिप्पणी आदि है। यानी आपको रीडएक्सएमएल () पद्धति को किसी ऐसी चीज के साथ समाप्त करना चाहिए जो चेक करती है कि आप अंतिम तत्व के माध्यम से पढ़ते हैं? वर्तमान में यह अंतिम पढ़ें () ऐसा करता है, लेकिन यह हमेशा नहीं हो सकता है।
रोरी

7
@ रोरी - नमूना जोड़ा। देर आए दुरुस्त आए?
ग्रेग

@ अच्छी जानकारी। क्या आप यह भी नहीं चाहेंगे कि ReadXml और WriteXml, Invariant Culture का उपयोग करें? मुझे लगता है कि यदि आप उपयोगकर्ता किसी अन्य देश में चले गए और अपने क्षेत्र और भाषा सेटिंग्स को बदल दिया तो आप समस्याओं में भाग सकते हैं। उस स्थिति में कोड सही ढंग से डीसर्विलाइज़ नहीं हो सकता है। मैंने पढ़ा है कि क्रमबद्धता करते समय हमेशा अपरिवर्तनीय संस्कृति का उपयोग करना सबसे अच्छा अभ्यास है
सार्वजनिक वायरलेस

जवाबों:


100

हां, GetSchema () को अशक्त लौटना चाहिए

IXmlSerializable.GetSchema विधि यह विधि आरक्षित है और इसका उपयोग नहीं किया जाना चाहिए। IXmlSerializable इंटरफ़ेस लागू करते समय, आपको इस विधि से एक शून्य संदर्भ (कुछ भी नहीं Visual Basic) वापस करना चाहिए, और इसके बजाय, यदि कोई कस्टम स्कीमा निर्दिष्ट करना आवश्यक है, तो वर्ग पर XmlSchemaProviderAttribute लागू करें।

पढ़ने और लिखने दोनों के लिए, ऑब्जेक्ट एलिमेंट पहले ही लिखा जा चुका है, इसलिए आपको लिखित में बाहरी तत्व जोड़ने की आवश्यकता नहीं है। उदाहरण के लिए, आप केवल दो में विशेषताएँ पढ़ना / लिखना शुरू कर सकते हैं।

के लिए लिखने :

आपके द्वारा प्रदत्त WriteXml कार्यान्वयन वस्तु के XML प्रतिनिधित्व को लिखना चाहिए। रूपरेखा एक आवरण तत्व लिखती है और इसकी शुरुआत के बाद XML लेखक को तैनात करती है। आपका कार्यान्वयन बाल सामग्री सहित इसकी सामग्री लिख सकता है। रूपरेखा फिर आवरण तत्व को बंद कर देती है।

और पढ़ने के लिए :

ReadXml विधि को लिखी गई जानकारी का उपयोग करके अपनी वस्तु को पुनर्गठित करना होगा जो कि WriteXml विधि द्वारा लिखा गया था।

जब इस पद्धति को कॉल किया जाता है, तो पाठक उस तत्व की शुरुआत में तैनात होता है जो आपके प्रकार के लिए जानकारी को लपेटता है। अर्थात, स्टार्ट टैग से ठीक पहले जो किसी क्रमबद्ध वस्तु की शुरुआत को इंगित करता है। जब यह विधि वापस आती है, तो उसने इसकी सभी सामग्रियों सहित, पूरे तत्व को शुरू से अंत तक पढ़ा होगा। WriteXml विधि के विपरीत, रूपरेखा आवरण तत्व को स्वचालित रूप से संभालती नहीं है। आपका कार्यान्वयन ऐसा करना चाहिए। इन पोजिशनिंग नियमों का पालन करने में असफल होने के कारण कोड अप्रत्याशित रनटाइम अपवाद या दूषित डेटा उत्पन्न कर सकता है।

मैं सहमत हूँ कि यह थोड़ा अस्पष्ट है, लेकिन यह " Read()रैपर के अंतिम-तत्व टैग के लिए आपका काम है" के लिए उबलता है ।


ईवेंट तत्वों को लिखने और पढ़ने के बारे में क्या? यह प्रारंभिक तत्व को मैन्युअल रूप से लिखने के लिए हैकिश लगता है। मुझे लगता है कि मैंने देखा है कि प्रत्येक बच्चे के तत्व को लिखने के लिए लिखने की विधि में एक XmlSerializer का उपयोग करें।
ग्रेग

@Greg; या तो उपयोग ठीक है ... हाँ, यदि आप की जरूरत है, तो आप एक नेस्टेड XmlSerializer का उपयोग कर सकते हैं, लेकिन यह एकमात्र विकल्प नहीं है।
मार्क Gravell

3
इन प्राथमिकताओं के लिए धन्यवाद, MSDN के अंदर का नमूना कोड इस बारे में बहुत ही अप्रयुक्त और अस्पष्ट है। मैं कई बार अटक गया और पढ़ने / लिखने के असममित व्यवहार के बारे में सोच रहा था।
जद्दन

1
@MarcGravell मुझे पता है कि यह एक पुराना धागा है। "फ्रेमवर्क एक रैपर तत्व लिखता है और एक्सएमएल लेखक को इसके शुरू होने के बाद रखता है।" यहीं पर संघर्ष कर रहा हूं। क्या रैपर को स्वचालित रूप से संभालने के इस चरण को छोड़ने के लिए मजबूर करने का एक तरीका है? मेरे पास एक ऐसी स्थिति है जहां मुझे इस चरण को छोड़ने की आवश्यकता है: stackoverflow.com/questions/20885455/…
जेम्स

मेरी जानकारी के अनुसार करने के लिए नहीं @James
मार्क Gravell

34

मैंने नमूनों पर इस विषय पर एक लेख लिखा था क्योंकि MSDN प्रलेखन अब तक काफी अस्पष्ट है और आप वेब पर जो उदाहरण पा सकते हैं, वे अधिकांश समय गलत तरीके से लागू होते हैं।

Mark Gravell ने जो पहले उल्लेख किया था, उसके बगल में स्थानों और खाली तत्वों के नुकसान

http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx


बहुत बढ़िया लेख! जब भी मैं कुछ डेटा को क्रमबद्ध करना चाहूंगा, तो मैं निश्चित रूप से इसका संदर्भ लूंगा।
ग्रेग

धन्यवाद! सकारात्मक प्रतिक्रिया की मात्रा इसे लिखने में निवेश किए गए समय की राशि को पुरस्कृत करती है। मैं गहराई से सराहना करता हूं कि आप इसे पसंद करते हैं! कुछ बिंदुओं पर आलोचना करने के लिए कहने में संकोच न करें।
जद्दन

MSDN के हवाले से उदाहरण अधिक उपयोगी हैं।

कोडप्रोजेक्ट के लिए धन्यवाद, मैं उसे वोट करूंगा, अगर मैं कर सकता था। विशेषताओं पर सामान MSDN की तुलना में पूरी तरह से व्यापक था। उदाहरण के लिए, मेरा: IXMLSerializable वर्ग तब टूट गया जब xsd.exe द्वारा प्रीफ़िक्स किया गया [Serializable (), XmlType (Namespace = "MonitorService")]।
जॉन

8

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

हमारा समाधान अपने स्वयं के IXmlSerializableइंटरफ़ेस को परिभाषित करना था , जो सिस्टम एक से निकला था, जिसमें एक विधि कहा गया था WriteOuterXml()। जैसा कि आप अनुमान लगा सकते हैं, यह विधि बस बाहरी तत्व लिखेगा, फिर कॉल करेगा WriteXml(), फिर तत्व का अंत लिख सकता है। बेशक, सिस्टम एक्सएमएल सिरीलाइज़र इस विधि को नहीं कहेगा, इसलिए यह केवल तब उपयोगी था जब हमने अपना सीरियलाइज़ेशन किया था, ताकि आपके मामले में सहायक हो या न हो। इसी तरह, हमने एक ReadContentXml()विधि जोड़ी , जिसमें बाहरी तत्व, केवल इसकी सामग्री नहीं पढ़ी।


5
C # 3.0 के साथ आप संभवतः इसके बजाय एक एक्सटेंशन विधि लिखकर कर सकते हैं, लेकिन एक दिलचस्प विचार।
मार्क Gravell

2

यदि आपके पास पहले से ही अपनी कक्षा का एक XmlDocument प्रतिनिधित्व है या XML संरचनाओं के साथ काम करने का XmlDocument तरीका पसंद करते हैं, तो IXmlSerializable को लागू करने का एक त्वरित और गंदा तरीका विभिन्न कार्यों के लिए बस इस xmldoc को पास करना है।

चेतावनी: XmlDocument (और / या XDocument) xmlreader / लेखक की तुलना में धीमेपन का एक क्रम है, इसलिए यदि प्रदर्शन एक परम आवश्यकता है, तो यह समाधान आपके लिए नहीं है!

class ExampleBaseClass : IXmlSerializable { 
    public XmlDocument xmlDocument { get; set; }
    public XmlSchema GetSchema()
    {
        return null;
    }
    public void ReadXml(XmlReader reader)
    {
        xmlDocument.Load(reader);
    }

    public void WriteXml(XmlWriter writer)
    {
        xmlDocument.WriteTo(writer);
    }
}

0

इंटरफ़ेस कार्यान्वयन अन्य उत्तरों द्वारा कवर किया गया है, लेकिन मैं रूट तत्व के लिए अपने 2-सेंट में टॉस करना चाहता था।

मैंने मूल तत्व को मेटाडेटा के रूप में रखना पसंद किया है। इसके कुछ लाभ हैं:

  • यदि कोई अशक्त वस्तु है, तो यह अभी भी क्रमबद्ध हो सकती है
  • एक कोड पठनीयता के दृष्टिकोण से, यह समझ में आता है

नीचे एक क्रमबद्ध शब्दकोश का उदाहरण दिया गया है, जहां शब्दकोश मूल तत्व को इस तरह से परिभाषित किया गया है:

using System.Collections.Generic;

[System.Xml.Serialization.XmlRoot("dictionary")]
public partial class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, System.Xml.Serialization.IXmlSerializable
{
            public virtual System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public virtual void ReadXml(System.Xml.XmlReader reader)
    {
        var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
        var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();
        if (wasEmpty)
            return;
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");
            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
            Add(key, value);
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        reader.ReadEndElement();
    }

    public virtual void WriteXml(System.Xml.XmlWriter writer)
    {
        var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
        var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
        foreach (TKey key in Keys)
        {
            writer.WriteStartElement("item");
            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();
            writer.WriteStartElement("value");
            var value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    }

    public SerializableDictionary() : base()
    {
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary)
    {
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer)
    {
    }

    public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer)
    {
    }

    public SerializableDictionary(int capacity) : base(capacity)
    {
    }

    public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer)
    {
    }

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.