Lettura di Xml con XmlReader in C #

Sto cercando di leggere il seguente documento Xml il più velocemente ansible e lasciare che le classi aggiuntive gestiscano la lettura di ogni sotto-blocco.

          

Tuttavia, sto provando a utilizzare l’object XmlReader per leggere ciascun account e successivamente “StatementsAvailable”. Suggerite di utilizzare XmlReader.Read e controllare ciascun elemento e gestirlo?

Ho pensato di separare le mie classi per gestire correttamente ciascun nodo. Quindi esiste una class AccountBase che accetta un’istanza XmlReader che legge NameOfKin e diverse altre proprietà sull’account. Poi volevo interagire con le Dichiarazioni e lasciare che un’altra class si occupasse della Dichiarazione (e successivamente la aggiungesse a un IList).

Finora ho fatto la parte “per class” facendo XmlReader.ReadElementString () ma non posso allenarmi a dire al puntatore di spostarsi sull’elemento StatementsDisponibile e lasciarmi scorrere attraverso loro e lasciare che un’altra class legga ognuna di quelle proeprties .

Sembra facile!

    La mia esperienza con XmlReader è che è molto facile leggere accidentalmente troppo. So che hai detto che vuoi leggerlo il più rapidamente ansible, ma invece hai provato a utilizzare un modello DOM? Ho trovato che LINQ to XML rende XML molto più semplice.

    Se il tuo documento è particolarmente grande, puoi combinare XmlReader e LINQ in XML creando un XElement da XmlReader per ciascuno dei tuoi elementi “esterni” in modo streaming: questo ti consente di eseguire la maggior parte del lavoro di conversione in LINQ in XML, ma ancora solo bisogno di una piccola porzione del documento in memoria in qualsiasi momento. Ecco alcuni esempi di codice (leggermente adattati da questo post del blog ):

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

    L’ho usato per convertire i dati utente di StackOverflow (che è enorme) in un altro formato prima – funziona molto bene.

    EDIT da radarbob, riformattato da Jon – anche se non è chiaro quale problema “leggi troppo lontano” viene riferito a …

    Questo dovrebbe semplificare il nesting e occuparsi del problema “a read too far”.

     using (XmlReader reader = XmlReader.Create(inputUrl)) { reader.ReadStartElement("theRootElement"); while (reader.Name == "TheNodeIWant") { XElement el = (XElement) XNode.ReadFrom(reader); } reader.ReadEndElement(); } 

    Questo si occupa di un problema “letto troppo lontano” perché implementa il classico pattern while while:

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

    Tre anni dopo, forse con la rinnovata enfasi su WebApi e dati xml, mi sono imbattuto in questa domanda. Dato che sono codewise, sono propenso a seguire Skeet da un aereo senza paracadute, e vedendo il suo codice iniziale doppiamente corroborato dall’articolo del team MS Xml e un esempio in BOL Streaming Transform di Large Xml Docs , ho rapidamente trascurato gli altri commenti , in particolare da ‘pbz’, che ha sottolineato che se si hanno gli stessi elementi per nome in successione, tutti gli altri vengono saltati a causa della doppia lettura. In effetti, gli articoli del blog BOL e MS stavano entrambi analizzando i documenti di origine con elementi di destinazione nidificati più in profondità del secondo livello, mascherando questo effetto collaterale.

    Le altre risposte risolvono questo problema. Volevo solo offrire una revisione leggermente più semplice che sembra funzionare bene finora, e tiene conto che l’xml potrebbe provenire da fonti diverse, non solo da un uri, e quindi l’estensione funziona su XmlReader gestito dall’utente. L’unica ipotesi è che il lettore sia nel suo stato iniziale, poiché altrimenti il ​​primo ‘Read ()’ potrebbe avanzare oltre un nodo desiderato:

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

    Facciamo questo tipo di analisi XML tutto il tempo. La chiave sta definendo dove il metodo di analisi lascerà il lettore in uscita. Se lasci sempre il lettore sull’elemento successivo che segue l’elemento che è stato letto per la prima volta, puoi leggere in modo sicuro e prevedibile nel stream XML. Quindi se il lettore sta indicizzando l’elemento , dopo l’analisi il lettore indicizzerà il tag di chiusura .

    Il codice di analisi è simile a questo:

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

    La class Statements legge solo nel nodo

     public class Statements { List _statements = new List(); 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; } } } } 

    La class Statement sembrerebbe molto simile

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

    Per gli oggetti secondari, ReadSubtree() fornisce un xml-reader limitato agli oggetti secondari, ma penso davvero che lo stai facendo nel modo più difficile. A meno che tu non abbia requisiti molto specifici per la gestione di xml inusuali / imprevedibili, usa XmlSerializer (magari abbinato a sgen.exe se lo desideri).

    XmlReader è … ingannevole. Contrasto a:

     using System; using System.Collections.Generic; using System.Xml.Serialization; public class ApplicationPool { private readonly List accounts = new List(); public List Accounts {get{return accounts;}} } public class Account { public string NameOfKin {get;set;} private readonly List statements = new List(); public List 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 {}}}} }); } } 

    L’esempio seguente naviga attraverso il stream per determinare il tipo di nodo corrente e quindi utilizza XmlWriter per generare il contenuto di XmlReader.

      StringBuilder output = new StringBuilder(); String xmlString = @"   test with a child element  stuff "; // 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(); 

    Nell’esempio seguente vengono utilizzati i metodi XmlReader per leggere il contenuto di elementi e attributi.

     StringBuilder output = new StringBuilder(); String xmlString = @"  The Autobiography of Benjamin Franklin  Benjamin Franklin  8.99  "; // 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(); 

    Non sono esperto. Ma penso che XmlReader non sia necessario. È molto difficile da usare.
    XElement è molto facile da usare.
    Se hai bisogno di prestazioni (più veloce), devi modificare il formato del file e utilizzare le classi StreamReader e StreamWriter.

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

    È ansible eseguire il ciclo attraverso xmlnode e ottenere i dati …… Lettore XML C #