Frage Lesen von Xml mit XmlReader in C #


Ich versuche, das folgende Xml-Dokument so schnell wie möglich zu lesen und lasse zusätzliche Klassen das Lesen jedes Unterblocks verwalten.

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

Ich versuche jedoch, das XmlReader-Objekt zu verwenden, um jedes Konto und anschließend die "StatementsAvailable" zu lesen. Empfehlen Sie die Verwendung von XmlReader.Read und überprüfen Sie jedes Element und behandeln Sie es?

Ich habe darüber nachgedacht, meine Klassen zu trennen, um jeden Knoten richtig zu behandeln. Daher gibt es eine AccountBase-Klasse, die eine XmlReader-Instanz akzeptiert, die das NameOfKin und mehrere andere Eigenschaften des Accounts liest. Dann wollte ich durch die Statements interagieren und sich von einer anderen Klasse über die Statement (und fügen Sie sie anschließend zu einer IList) ergänzen.

Bis jetzt habe ich den "pro Klasse" -Teil gemacht, indem ich XmlReader.ReadElementString () gemacht habe, aber ich kann nicht trainieren, wie ich den Zeiger zum StatementsAvailable-Element bewegen kann und lasse mich durch sie hindurchgehen und lasse eine andere Klasse jede dieser Aufgaben lesen .

Klingt einfach!


76
2018-03-14 09:06


Ursprung


Antworten:


Meine Erfahrung von XmlReader ist, dass es sehr einfach ist, versehentlich zu viel zu lesen. Ich weiß, du hast gesagt, du möchtest es so schnell wie möglich lesen, aber du hast es versucht Verwenden Sie stattdessen ein DOM-Modell? Ich habe festgestellt, dass LINQ to XML XML viel Arbeit macht viel einfacher.

Wenn Ihr Dokument besonders groß ist, können Sie kombinieren XmlReader und LINQ to XML durch Erstellen eines XElement von einem XmlReader Für jedes Ihrer "äußeren" Elemente im Streaming-Modus: Dadurch können Sie die meiste Konvertierungsarbeit in LINQ to XML erledigen, benötigen aber immer noch nur einen kleinen Teil des Dokuments im Speicher. Hier ist ein Beispielcode (leicht angepasst von dieser Blogbeitrag):

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

Ich habe dies verwendet, um die StackOverflow-Benutzerdaten (die enorm sind) in ein anderes Format vorher zu konvertieren - es funktioniert sehr gut.

EDIT vom radarbob, neu formatiert von Jon - obwohl es nicht ganz klar ist, welches "zu weit gelesen" -Problem angesprochen wird ...

Dies sollte die Verschachtelung vereinfachen und das "a read too far" Problem beheben.

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

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

    reader.ReadEndElement();
}

Dies behebt das Problem "a read too far", da es das klassische while-loop-Muster implementiert:

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

138
2018-03-14 09:17



Drei Jahre später, vielleicht mit der erneuten Betonung von WebApi und XML-Daten, stieß ich auf diese Frage. Seit Code bin ich geneigt, Skeet aus einem Flugzeug ohne einen Fallschirm zu folgen, und seinen Anfangscode doppelt durch den MS Xml Teamartikel sowie ein Beispiel in BOL corraboriert zu sehen Streaming-Transformation großer Xml-DokumenteIch habe sehr schnell die anderen Kommentare übersehen, vor allem von 'pbz', die darauf hinwiesen, dass wenn man die gleichen Elemente nacheinander namentlich nennt, jeder zweite aufgrund des doppelten Lesens übersprungen wird. Und in der Tat haben die BOL- und MS-Blog-Artikel beide Quelldokumente analysiert, wobei Zielelemente tiefer als auf der zweiten Ebene verschachtelt waren, was diesen Nebeneffekt maskierte.

Die anderen Antworten adressieren dieses Problem. Ich wollte nur eine etwas einfachere Version anbieten, die bisher gut funktioniert, und berücksichtigt, dass das XML aus verschiedenen Quellen kommen kann, nicht nur aus einem URI, und die Erweiterung also auf dem vom Benutzer verwalteten XmlReader funktioniert. Die einzige Annahme ist, dass sich der Leser in seinem Anfangszustand befindet, da andernfalls das erste 'Read ()' über einen gewünschten Knoten hinaus vorrücken könnte:

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

25
2017-10-03 17:38



Wir machen diese Art von XML-Parsing die ganze Zeit. Der Schlüssel ist zu definieren, wo die Parsing-Methode den Leser beim Beenden verlassen wird. Wenn Sie den Reader immer auf dem nächsten Element hinter dem Element lassen, das zuerst gelesen wurde, können Sie den XML-Stream sicher und vorhersehbar einlesen. Also, wenn der Leser gerade den Index indexiert <Account> Element, nach dem Parsen des Lesers indexiert die </Accounts> schließendes Tag

Der Parsing-Code sieht ungefähr so ​​aus:

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

Das Statements Klasse liest gerade in der <StatementsAvailable> Knoten

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

Das Statement Klasse würde sehr ähnlich aussehen

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

15
2018-03-14 09:41



Für Unterobjekte ReadSubtree() gibt Ihnen einen XML-Reader beschränkt auf die Unterobjekte, aber ich Ja wirklich denke, dass du das auf die harte Tour machst. Es sei denn du hast sehr spezifisch Anforderungen für den Umgang mit ungewöhnlichen / unvorhersehbaren XML, verwenden XmlSerializer (vielleicht gekoppelt mit sgen.exe wenn du wirklich willst).

XmlReader ist ... knifflig. Im Gegensatz zu:

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

5
2018-03-14 09:15



Das folgende Beispiel navigiert durch den Stream, um den aktuellen Knotentyp zu ermitteln, und verwendet dann XmlWriter, um den XmlReader-Inhalt auszugeben.

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

Im folgenden Beispiel werden die XmlReader-Methoden zum Lesen des Inhalts von Elementen und Attributen verwendet.

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

1
2018-04-14 07:49



Ich bin nicht experientiert. Aber ich denke XmlReader ist nicht notwendig. Es ist sehr schwer zu benutzen.
XElement ist sehr einfach zu bedienen.
Wenn Sie Leistung (schneller) benötigen, müssen Sie das Dateiformat ändern und StreamReader- und StreamWriter-Klassen verwenden.


0
2017-12-28 09:08



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

Sie können durch xmlnode durchlaufen und die Daten erhalten ...... C # XML Leser


-1
2018-04-03 05:52