C # में XmlReader के साथ Xml पढ़ना


97

मैं निम्नलिखित Xml दस्तावेज़ को जितनी जल्दी हो सके पढ़ने की कोशिश कर रहा हूं और अतिरिक्त कक्षाओं को प्रत्येक उप खंड के पढ़ने का प्रबंधन करने देता हूं।

<ApplicationPool>
    <Accounts>
        <Account>
            <NameOfKin></NameOfKin>
            <StatementsAvailable>
                <Statement></Statement>
            </StatementsAvailable>
        </Account>
    </Accounts>
</ApplicationPool>

हालाँकि, मैं प्रत्येक खाते को पढ़ने के लिए XmlReader ऑब्जेक्ट का उपयोग करने की कोशिश कर रहा हूं और बाद में "स्टेटमेंटएविलेबल"। क्या आप XmlReader.Read का उपयोग करने का सुझाव देते हैं और प्रत्येक तत्व की जांच करते हैं और इसे संभालते हैं?

मैंने प्रत्येक नोड को ठीक से संभालने के लिए अपनी कक्षाओं को अलग करने के बारे में सोचा है। तो एक खाताबेस वर्ग है जो एक XmlReader उदाहरण को स्वीकार करता है जो NameOfKin और खाते के बारे में कई अन्य गुणों को पढ़ता है। तब मैं स्टेटमेंट के माध्यम से हस्तक्षेप करना चाहता था और एक अन्य वर्ग को स्टेटमेंट के बारे में खुद को भरने देता था (और बाद में इसे एक इलिस्ट में जोड़ देता था)।

इस प्रकार अब तक मेरे पास "प्रति वर्ग" भाग XmlReader.ReadElementString () करके किया गया है, लेकिन मैं वर्कआउट नहीं कर सकता कि पॉइंटर को स्टेटमेंट्स एवलेबल एलीमेंट में जाने के लिए कैसे बताएं और मुझे उनके माध्यम से पुनरावृत्त होने दें और दूसरे को उन प्रत्येक प्रीपेट्रीज़ को पढ़ने दें। ।

आसान लगता है!


1
संपादन सहायता प्राप्त करने के लिए संपादन बॉक्स के ऊपरी दाएं कोने में नारंगी प्रश्न चिह्न पर क्लिक करें। संभवतः आप एक कोड ब्लॉक बनाना चाहते हैं, जो पहले एक रिक्त रेखा द्वारा किया जाता है और फिर प्रत्येक पंक्ति चार स्थानों के साथ इंडेंट की जाती है।
एंडर्स एबेल

या बस कोड / XML की अपनी पंक्तियों का चयन करें और फिर संपादक टूलबार में "कोड" बटन (101 010) पर क्लिक करें - जैसे कि सरल!
marc_s

जवाबों:


163

मेरा अनुभव XmlReaderयह है कि गलती से बहुत अधिक पढ़ना बहुत आसान है। मुझे पता है कि आपने कहा है कि आप इसे जितनी जल्दी हो सके पढ़ना चाहते हैं, लेकिन क्या आपने इसके बजाय DOM मॉडल का उपयोग करने की कोशिश की है ? मैंने पाया है कि LINQ to XML से XML का काम बहुत आसान हो जाता है।

यदि आपका दस्तावेज़ विशेष रूप से बहुत बड़ा है, तो आप को जोड़ सकते हैं XmlReaderएक बनाने के द्वारा एक्सएमएल के लिए और LINQ XElementएक से XmlReaderएक स्ट्रीमिंग तरीके से अपने "बाहरी" तत्वों से प्रत्येक के लिए: यह आप XML को LINQ में रूपांतरण अधिकांश काम की सुविधा देता है, लेकिन अभी भी केवल जरूरत किसी भी समय स्मृति में दस्तावेज़ का एक छोटा सा हिस्सा। यहाँ कुछ नमूना कोड ( इस ब्लॉग पोस्ट से थोड़ा अनुकूलित किया गया है ):

static IEnumerable<XElement> SimpleStreamAxis(string inputUrl,
                                              string elementName)
{
  using (XmlReader reader = XmlReader.Create(inputUrl))
  {
    reader.MoveToContent();
    while (reader.Read())
    {
      if (reader.NodeType == XmlNodeType.Element)
      {
        if (reader.Name == elementName)
        {
          XElement el = XNode.ReadFrom(reader) as XElement;
          if (el != null)
          {
            yield return el;
          }
        }
      }
    }
  }
}

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

रडार से ईडीआईटी, जॉन द्वारा सुधारित - हालांकि यह बिल्कुल स्पष्ट नहीं है जो "बहुत दूर पढ़ा" समस्या को संदर्भित किया जा रहा है ...

यह घोंसले के शिकार को सरल करना चाहिए और "बहुत दूर पढ़ने में" समस्या का ध्यान रखना चाहिए।

using (XmlReader reader = XmlReader.Create(inputUrl))
{
    reader.ReadStartElement("theRootElement");

    while (reader.Name == "TheNodeIWant")
    {
        XElement el = (XElement) XNode.ReadFrom(reader);
    }

    reader.ReadEndElement();
}

यह "एक पढ़ने बहुत दूर" समस्या का ख्याल रखता है क्योंकि यह लूप पैटर्न के दौरान क्लासिक को लागू करता है:

initial read;
(while "we're not at the end") {
    do stuff;
    read;
}

17
कॉलिंग XNode.ReadFrom तत्व को पढ़ता है और अगले एक पर जाता है, फिर निम्न रीडर। रीड () अगले एक को फिर से पढ़ता है। यदि वे एक ही नाम रखते हैं और लगातार बने रहते हैं तो आप अनिवार्य रूप से एक तत्व को याद करेंगे।
pbz

3
@pbz: धन्यवाद। मुझे यकीन नहीं है कि मैं इसे सही तरीके से संपादित करने के लिए खुद पर भरोसा करता हूं (कि मैं XmlReader को कितना नापसंद करता हूं :) क्या आप इसे सही तरीके से संपादित करने में सक्षम हैं?
जॉन स्कीट

1
@JonSkeet - मुझे कुछ याद आ रहा है लेकिन बस pbz द्वारा बताए गए मुद्दे if(reader.Name == elementName)को while(reader.Name == elementName)ठीक करने के लिए बदलना नहीं चाहिए?
डेविड मैकलीन

1
@pbz: मैंने लाइन बदल दी: XElement el = XNode.ReadFrom (रीडर) XElement के रूप में; होने के लिए: XElement el = XElement.Load (Reader.ReadSubtree ()); चूँकि यह लगातार तत्वों को लंघन को ठीक करता है।
डायलन हॉग

1
जैसा कि अन्य टिप्पणियों में उल्लेख किया गया है, SimpleStreamAxis()XML का इंडेंट न होने पर तत्वों का वर्तमान संस्करण छोड़ देगा, क्योंकि लोड किए गए तत्व के बादNode.ReadFrom() अगले नोड पर पाठक को तैनात करता है - जो अगले बिना शर्त द्वारा छोड़ दिया जाएगा । अगर अगला नोड व्हॉट्सएप है तो सब ठीक है। नहीं तो नहीं। इस समस्या के बिना संस्करणों के लिए यहां , यहां या यहां देखेंRead()
dbc

29

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

अन्य उत्तर इस समस्या का समाधान करते हैं। मैं बस थोड़ा सरल संशोधन की पेशकश करना चाहता था जो अब तक अच्छी तरह से काम करता है, और इस बात को ध्यान में रखता है कि xml अलग-अलग स्रोतों से आ सकता है, न कि केवल एक uri, और इसलिए उपयोगकर्ता XmlReader को प्रबंधित करता है। एक धारणा यह है कि पाठक अपनी प्रारंभिक स्थिति में है, अन्यथा पहले 'पढ़ें ()' वांछित नोड से आगे बढ़ सकता है:

public static IEnumerable<XElement> ElementsNamed(this XmlReader reader, string elementName)
{
    reader.MoveToContent(); // will not advance reader if already on a content node; if successful, ReadState is Interactive
    reader.Read();          // this is needed, even with MoveToContent and ReadState.Interactive
    while(!reader.EOF && reader.ReadState == ReadState.Interactive)
    {
        // corrected for bug noted by Wes below...
        if(reader.NodeType == XmlNodeType.Element && reader.Name.Equals(elementName))
        {
             // this advances the reader...so it's either XNode.ReadFrom() or reader.Read(), but not both
             var matchedElement = XNode.ReadFrom(reader) as XElement;
             if(matchedElement != null)
                 yield return matchedElement;
        }
        else
            reader.Read();
    }
}

1
आपका "if (Reader.Name.Equals (elementName))" स्टेटमेंट एक संबंधित "अन्य रीडर। रीड (रीड) को याद कर रहा है;" बयान। यदि तत्व वह नहीं है जो आप चाहते हैं कि आप पढ़ना जारी रखना चाहते हैं। यही कारण है कि मुझे इसे मेरे लिए काम करने के लिए जोड़ना पड़ा।
वेस

1
@ दोनों शर्तों (NodeType और Name) को ध्वस्त करके समस्या को ठीक किया ताकि else Read()दोनों पर लागू हो। उस पकड़ने के लिए धन्यवाद।
मदिसिबियो

1
मैंने आपको उकसाया, लेकिन दो बार लिखी गई विधि विधि को देखकर मुझे बहुत खुशी नहीं हुई। हो सकता है कि आप यहाँ लूप करते समय इस्तेमाल कर सकते हैं? :)
नवाफ़ल २०'१५ को

एक अन्य जवाब जिसने MSDN डॉक्स के साथ एक ही समस्या को देखा और हल किया: stackoverflow.com/a/18282052/3744182
dbc

17

हम इस तरह के XML को हर समय पार्स करते हैं। कुंजी परिभाषित कर रही है जहां पार्सिंग विधि पाठक को बाहर निकलने पर छोड़ देगी। यदि आप हमेशा पहले पढ़ने वाले तत्व का अनुसरण करते हुए अगले तत्व पर पाठक को छोड़ देते हैं तो आप XML स्ट्रीम में सुरक्षित और अनुमानित रूप से पढ़ सकते हैं। इसलिए यदि पाठक वर्तमान में <Account>तत्व को अनुक्रमित कर रहा है, तो पाठक के पार्स करने के बाद </Accounts>समापन टैग को अनुक्रमित किया जाएगा ।

पार्सिंग कोड कुछ इस तरह दिखता है:

public class Account
{
    string _accountId;
    string _nameOfKin;
    Statements _statmentsAvailable;

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();

        // Read node attributes
        _accountId = reader.GetAttribute( "accountId" );
        ...

        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {
            if( reader.IsStartElement() )
            {
                switch( reader.Name )
                {
                    // Read element for a property of this class
                    case "NameOfKin":
                        _nameOfKin = reader.ReadElementContentAsString();
                        break;

                    // Starting sub-list
                case "StatementsAvailable":
                    _statementsAvailable = new Statements();
                    _statementsAvailable.Read( reader );
                    break;

                    default:
                        reader.Skip();
                }
            }
            else
            {
                reader.Read();
                break;
            }
        }       
    }
}

Statementsवर्ग बस में पढ़ता <StatementsAvailable>नोड

public class Statements
{
    List<Statement> _statements = new List<Statement>();

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();
        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {
            if( reader.IsStartElement() )
            {
                if( reader.Name == "Statement" )
                {
                    var statement = new Statement();
                    statement.ReadFromXml( reader );
                    _statements.Add( statement );               
                }
                else
                {
                    reader.Skip();
                }
            }
            else
            {
                reader.Read();
                break;
            }
        }
    }
}

Statementवर्ग बहुत ज्यादा एक ही विचार करेंगे

public class Statement
{
    string _satementId;

    public void ReadFromXml( XmlReader reader )
    {
        reader.MoveToContent();

        // Read noe attributes
        _statementId = reader.GetAttribute( "statementId" );
        ...

        if( reader.IsEmptyElement ) { reader.Read(); return; }

        reader.Read();
        while( ! reader.EOF )
        {           
            ....same basic loop
        }       
    }
}

6

उप-ऑब्जेक्ट्स के लिए, ReadSubtree()आपको उप-ऑब्जेक्ट्स तक सीमित एक एक्सएमएल-रीडर देता है, लेकिन मुझे वास्तव में लगता है कि आप इसे कठिन तरीके से कर रहे हैं। जब तक आपको असामान्य / अनपेक्षित xml से निपटने के लिए बहुत विशिष्ट आवश्यकताएं नहीं हैं, तब तक उपयोग करें XmlSerializer(शायद sgen.exeअगर आप वास्तव में चाहते हैं तो इसके साथ युग्मित करें )।

XmlReaderहै ... मुश्किल। इसके विपरीत:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
public class ApplicationPool {
    private readonly List<Account> accounts = new List<Account>();
    public List<Account> Accounts {get{return accounts;}}
}
public class Account {
    public string NameOfKin {get;set;}
    private readonly List<Statement> statements = new List<Statement>();
    public List<Statement> StatementsAvailable {get{return statements;}}
}
public class Statement {}
static class Program {
    static void Main() {
        XmlSerializer ser = new XmlSerializer(typeof(ApplicationPool));
        ser.Serialize(Console.Out, new ApplicationPool {
            Accounts = { new Account { NameOfKin = "Fred",
                StatementsAvailable = { new Statement {}, new Statement {}}}}
        });
    }
}

3

निम्न उदाहरण धारा नोड प्रकार निर्धारित करने के लिए धारा के माध्यम से नेविगेट करता है, और फिर XmlRriter का उपयोग करके XmlRderder सामग्री का उत्पादन करता है।

    StringBuilder output = new StringBuilder();

    String xmlString =
            @"<?xml version='1.0'?>
            <!-- This is a sample XML document -->
            <Items>
              <Item>test with a child element <more/> stuff</Item>
            </Items>";
    // Create an XmlReader
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
    {
        XmlWriterSettings ws = new XmlWriterSettings();
        ws.Indent = true;
        using (XmlWriter writer = XmlWriter.Create(output, ws))
        {

            // Parse the file and display each of the nodes.
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        writer.WriteStartElement(reader.Name);
                        break;
                    case XmlNodeType.Text:
                        writer.WriteString(reader.Value);
                        break;
                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                        writer.WriteProcessingInstruction(reader.Name, reader.Value);
                        break;
                    case XmlNodeType.Comment:
                        writer.WriteComment(reader.Value);
                        break;
                    case XmlNodeType.EndElement:
                        writer.WriteFullEndElement();
                        break;
                }
            }

        }
    }
    OutputTextBlock.Text = output.ToString();

निम्नलिखित उदाहरण तत्वों और विशेषताओं की सामग्री को पढ़ने के लिए XmlReader विधियों का उपयोग करता है।

StringBuilder output = new StringBuilder();

String xmlString =
    @"<bookstore>
        <book genre='autobiography' publicationdate='1981-03-22' ISBN='1-861003-11-0'>
            <title>The Autobiography of Benjamin Franklin</title>
            <author>
                <first-name>Benjamin</first-name>
                <last-name>Franklin</last-name>
            </author>
            <price>8.99</price>
        </book>
    </bookstore>";

// Create an XmlReader
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
    reader.ReadToFollowing("book");
    reader.MoveToFirstAttribute();
    string genre = reader.Value;
    output.AppendLine("The genre value: " + genre);

    reader.ReadToFollowing("title");
    output.AppendLine("Content of the title element: " + reader.ReadElementContentAsString());
}

OutputTextBlock.Text = output.ToString();

0
    XmlDataDocument xmldoc = new XmlDataDocument();
    XmlNodeList xmlnode ;
    int i = 0;
    string str = null;
    FileStream fs = new FileStream("product.xml", FileMode.Open, FileAccess.Read);
    xmldoc.Load(fs);
    xmlnode = xmldoc.GetElementsByTagName("Product");

आप xmlnode के माध्यम से लूप कर सकते हैं और डेटा प्राप्त कर सकते हैं ...... C # XML रीडर


4
यह वर्ग पदावनत है। प्रयोग नहीं करें।
नवाफाल

@Elvarism आपके द्वारा साझा की जाने वाली वेबसाइट में कई अन्य पढ़े गए xml तरीके हैं, और जो मुझे बहुत मदद करते हैं। मैं आपको वोट दूंगा। यहाँ एक और आसानी से समझ में आने वाला XmlReader उदाहरण है।
劉鎮 劉鎮

0

मैं अनुभवात्मक नहीं हूँ। लेकिन मुझे लगता है कि XmlReader अनावश्यक है। इसका उपयोग करना बहुत कठिन है।
XElement का उपयोग करना बहुत आसान है।
यदि आपको प्रदर्शन (तेज) की आवश्यकता है, तो आपको फ़ाइल प्रारूप को बदलना होगा और स्ट्रीमराइडर और स्ट्रीमविटर कक्षाओं का उपयोग करना होगा।

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