Lendo Xml com XmlReader em c #

Estou tentando ler o seguinte documento Xml o mais rápido possível e permitir que classs adicionais gerenciem a leitura de cada sub bloco.

          

No entanto, estou tentando usar o object XmlReader para ler cada conta e, posteriormente, o “StatementsAvailable”. Você sugere usar XmlReader.Read e verificar cada elemento e lidar com isso?

Eu pensei em separar minhas classs para lidar com cada nó corretamente. Portanto, há uma class AccountBase que aceita uma instância XmlReader que lê o NameOfKin e várias outras propriedades sobre a conta. Então eu estava querendo interar as Declarações e deixar uma outra class se completar sobre a Declaração (e subseqüentemente adicioná-la a um IList).

Até agora eu tenho a parte “por class” feita fazendo XmlReader.ReadElementString () mas eu não posso treinar como dizer ao ponteiro para mover para o elemento StatementsAvailable e deixe-me iterar através deles e deixar outra class ler cada uma dessas propriedades .

Parece fácil!

    Minha experiência com o XmlReader é que é muito fácil ler acidentalmente demais. Eu sei que você disse que quer lê-lo o mais rápido possível, mas você já tentou usar um modelo DOM? Eu descobri que LINQ to XML faz o XML funcionar muito mais fácil.

    Se o seu documento é particularmente grande, você pode combinar XmlReader e LINQ para XML, criando um XElement de um XmlReader para cada um dos seus elementos “externos” de forma contínua: isso permite fazer a maior parte do trabalho de conversão no LINQ para XML, mas ainda precisa apenas de uma pequena parte do documento na memory a qualquer momento. Aqui está um exemplo de código (adaptado ligeiramente desta postagem do 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; } } } } } } 

    Eu usei isso para converter os dados do usuário StackOverflow (que é enorme) em outro formato antes – funciona muito bem.

    EDIT do radarbob, reformatado por Jon – embora não esteja claro qual problema “leia muito longe” está sendo referido …

    Isso deve simplificar o aninhamento e cuidar do problema “uma leitura muito distante”.

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

    Isso cuida de um problema “leia muito longe” porque implementa o padrão clássico de loop while:

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

    Três anos depois, talvez com a ênfase renovada nos dados WebApi e xml, me deparei com essa questão. Já que estou disposto a seguir Skeet de um avião sem pára-quedas, e ver seu código inicial duplamente elaborado pelo artigo da equipe do MS Xml, bem como um exemplo em BOL Transmição de Transmissão de Grandes Xml Docs , rapidamente esqueci os outros comentários , mais especificamente a partir de ‘pbz’, que apontou que se você tem os mesmos elementos pelo nome em sucessão, todos os outros são ignorados por causa da leitura dupla. E, de fato, os artigos de blog do BOL e do MS estavam analisando documentos de origem com elementos de destino nesteds mais profundamente do que o segundo nível, mascarando esse efeito colateral.

    As outras respostas resolvem esse problema. Eu só queria oferecer uma revisão um pouco mais simples que parece funcionar bem até agora, e leva em conta que o xml pode vir de fonts diferentes, não apenas de um uri, e então a extensão funciona no XmlReader gerenciado pelo usuário. A única suposição é que o leitor está em seu estado inicial, pois, do contrário, o primeiro ‘Read ()’ pode avançar além do nó desejado:

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

    Nós fazemos esse tipo de análise XML o tempo todo. A chave é definir onde o método de análise deixará o leitor na saída. Se você sempre deixar o leitor no próximo elemento após o elemento que foi lido pela primeira vez, poderá ler com segurança e previsivelmente no stream XML. Portanto, se o leitor estiver atualmente indexando o elemento , após a análise, o leitor indexará a tag de fechamento .

    O código de análise é algo como isto:

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

    A class Statements apenas lê o nó

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

    A class Statement ficaria muito parecida

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

    Para sub-objects, o ReadSubtree() fornece um leitor xml limitado aos sub-objects, mas eu realmente acho que você está fazendo isso da maneira mais difícil. A menos que você tenha requisitos muito específicos para lidar com xml incomum / imprevisível, use o XmlSerializer (talvez associado ao sgen.exe se você realmente quiser).

    XmlReader é … complicado. Contrasta com:

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

    O exemplo a seguir navega pelo stream para determinar o tipo de nó atual e, em seguida, usa XmlWriter para gerar o conteúdo XmlReader.

      StringBuilder output = new StringBuilder(); String xmlString = @"< ?xml version='1.0'?>   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(); 

    O exemplo a seguir usa os methods XmlReader para ler o conteúdo de elementos e atributos.

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

    Eu não sou experiente. Mas eu acho que o XmlReader é desnecessário. É muito difícil de usar.
    XElement é muito fácil de usar.
    Se você precisar de desempenho (mais rápido), você deve alterar o formato de arquivo e usar as classs 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"); 

    Você pode percorrer xmlnode e obter os dados …… C # XML Reader