قراءة Xml مع XmlReader في C#




(5)

أحاول قراءة مستند Xml التالي بأسرع ما يمكنني ، واسمح لفصول إضافية بإدارة قراءة كل كتلة فرعية.

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

ومع ذلك ، أحاول استخدام كائن XmlReader لقراءة كل حساب ثم "StatementsAvailable". هل تقترح استخدام XmlReader.Read وتحقق من كل عنصر والتعامل معها؟

لقد فكرت في فصل فصولي للتعامل مع كل عقدة بشكل صحيح. لذلك هناك theres فئة AccountBase التي تقبل مثيل XmlReader يقرأ في NameOfKin وعدة خصائص أخرى حول الحساب. ثم كنت أرغب في التفاعل من خلال البيانات ودع فئة أخرى تملأ نفسها حول البيان (وإضافتها لاحقا إلى إيليست).

حتى الآن لدي جزء "لكل فئة" تم تنفيذه من خلال تنفيذ XmlReader.ReadElementString () ولكن لا يمكنني تدريب كيفية إخبار المؤشر بالانتقال إلى عنصر StatementsAvailable والسماح لي بالتكرار من خلالهم والسماح لفصل آخر بقراءة كل هذه العناوين .

يبدو سهلا!


يتنقل المثال التالي خلال الدفق لتحديد نوع العقدة الحالية ، ثم يستخدم XmlWriter لإخراج محتوى XmlReader.

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

أنا لست مختبرا ، ولكن أعتقد أن XmlReader غير ضروري. من الصعب جدا استخدام.
XElement سهل جدا للاستخدام.
إذا كنت بحاجة إلى أداء (أسرع) ، يجب عليك تغيير تنسيق الملف واستخدام فئات StreamReader و StreamWriter.


بعد ثلاث سنوات ، ربما مع التركيز المتجدد على بيانات WebApi و xml ، صادفت هذا السؤال. منذ الصيغ أنا أميل إلى اتباع Skeet للخروج من طائرة دون المظلة ، ورؤية شفرته الأولية تتعرض للمضايقات بشكل مضاعف من قبل مقالة فريق MS Xml ، فضلا عن مثال في BOL Streaming Transform من مستندات XML كبيرة ، أغفلت بسرعة كبيرة التعليقات الأخرى ، على الأخص من "pbz" ، الذي أشار إلى أنه إذا كان لديك نفس العناصر بالاسم في الخلافة ، يتم تخطي كل واحد آخر بسبب القراءة المزدوجة. وفي الواقع ، كانت مقالات مدونة BOL و MS تحلل مستندات المصدر مع عناصر الهدف متداخلة أعمق من المستوى الثاني ، مما يخفي هذا الأثر الجانبي.

الإجابات الأخرى تتناول هذه المشكلة. أردت فقط تقديم مراجعة أبسط قليلاً يبدو أنها تعمل بشكل جيد حتى الآن ، وتأخذ في الاعتبار أن 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();
    }
}

تجربتي مع XmlReader هي أنه من السهل جدًا القراءة عن طريق الخطأ أكثر من اللازم. أعلم أنك قلت أنك تريد قراءته في أسرع وقت ممكن ، ولكن هل حاولت استخدام نموذج DOM بدلاً من ذلك؟ لقد وجدت أن LINQ إلى XML يجعل XML يعمل أسهل بكثير .

إذا كان مستندك ضخمًا بشكل خاص ، فيمكنك الجمع بين XmlReader و LINQ إلى XML عن طريق إنشاء XElement من XmlReader لكل عنصر من العناصر "الخارجية" الخاصة بك بطريقة تدفق: يتيح لك ذلك تنفيذ معظم أعمال التحويل في LINQ إلى XML ، ولكن لا تزال تحتاج فقط إلى جزء صغير من المستند في الذاكرة في أي وقت. إليك بعض نماذج التعليمة البرمجية (تم تعديلها بشكل طفيف من مشاركة المدونة هذه ):

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

لقد استخدمت هذا لتحويل بيانات المستخدم (وهي هائلة) إلى تنسيق آخر من قبل - وهو يعمل بشكل جيد للغاية.

تحرير من radarbob ، وإعادة تنسيقها من قبل جون - على الرغم من أنه ليس من الواضح تماما أي مشكلة "قراءة أكثر من اللازم" يتم الإشارة إلى ...

هذا يجب أن يبسط التعشيش ويعتني بمشكلة "قراءة بعيدة جداً".

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

    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 Reader





xmlreader