J'essaie de lire aussi rapidement que possible le document XML suivant et de laisser des classes supplémentaires gérer la lecture de chaque sous-bloc.
<ApplicationPool>
<Accounts>
<Account>
<NameOfKin></NameOfKin>
<StatementsAvailable>
<Statement></Statement>
</StatementsAvailable>
</Account>
</Accounts>
</ApplicationPool>
Cependant, j'essaie d'utiliser l'objet XmlReader pour lire chaque compte, puis le "StatementsAvailable". Suggérez-vous d’utiliser XmlReader.Read, vérifiez chaque élément et gérez-le?
J'ai pensé à séparer mes classes pour gérer chaque nœud correctement. Il existe donc une classe AccountBase qui accepte une instance de XmlReader qui lit NameOfKin et plusieurs autres propriétés du compte. Ensuite, je voulais interagir à travers les déclarations et laisser une autre classe se renseigner sur la déclaration (et l'ajouter par la suite à une liste IList).
Jusqu'ici, j'ai terminé la partie "par classe" en faisant XmlReader.ReadElementString () mais je ne peux pas expliquer comment indiquer au pointeur de se déplacer vers l'élément StatementsAvailable et de me laisser le parcourir et laisser une autre classe lire chacune de ces propriétés. .
Cela semble facile!
Mon expérience de XmlReader
est qu’il est très facile de lire trop accidentellement. Je sais que vous avez dit que vous souhaitiez le lire le plus rapidement possible, mais avez-vous essayé d'utiliser un modèle DOM à la place? J'ai constaté que LINQ to XML facilite grandement le travail XML .
Si votre document est particulièrement volumineux, vous pouvez combiner XmlReader
et LINQ to XML en créant un XElement
à partir de XmlReader
pour chacun de vos éléments "extérieurs" de manière continue: cela vous permet de: La plupart des travaux de conversion fonctionnent dans LINQ to XML, mais ils n’ont toujours besoin que d’une petite partie du document en mémoire à la fois. Voici un exemple de code (adapté légèrement de cet article de blog ):
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;
}
}
}
}
}
}
J'ai déjà utilisé cela pour convertir les données utilisateur StackOverflow (qui est énorme) dans un autre format auparavant - cela fonctionne très bien.
EDIT de radarbob, reformaté par Jon - bien que l’on ne sache pas exactement à quel problème "lire trop loin" on parle ...
Cela devrait simplifier l’emboîtement et résoudre le problème "une lecture trop loin".
using (XmlReader reader = XmlReader.Create(inputUrl))
{
reader.ReadStartElement("theRootElement");
while (reader.Name == "TheNodeIWant")
{
XElement el = (XElement) XNode.ReadFrom(reader);
}
reader.ReadEndElement();
}
Cela résout le problème "une lecture trop loin" car il implémente le motif classique de la boucle while:
initial read;
(while "we're not at the end") {
do stuff;
read;
}
Trois ans plus tard, avec peut-être l’importance renouvelée accordée à WebApi et aux données XML, je suis tombé sur cette question. Depuis que je suis codément, je suis enclin à suivre Skeet dans un avion sans parachute, et à voir son code initial doublement corrodé par l'article de l'équipe MS Xml ainsi qu'un exemple dans BOL Transformation en streaming de documents grand format XML , J'ai très vite négligé les autres commentaires, plus particulièrement ceux de "pbz", qui ont souligné que si vous avez les mêmes éléments nommément l'un après l'autre, tous les autres sont ignorés à cause de la double lecture. Et en fait, les articles de blog BOL et MS analysaient les documents source avec des éléments cibles imbriqués plus profondément que le deuxième niveau, masquant ainsi cet effet secondaire.
Les autres réponses abordent ce problème. Je voulais juste proposer une révision un peu plus simple qui semble bien fonctionner jusqu'à présent et qui tient compte du fait que le xml peut provenir de différentes sources, pas seulement d'un uri. L'extension fonctionne donc sur XmlReader géré par l'utilisateur. La première hypothèse est que le lecteur est dans son état initial, sinon le premier 'Read ()' pourrait dépasser un nœud souhaité:
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();
}
}
Nous faisons ce genre d'analyse XML tout le temps. La clé est de définir où la méthode d'analyse laissera le lecteur à la sortie. Si vous laissez toujours le lecteur sur l'élément suivant après l'élément qui a été lu en premier, vous pouvez alors lire de manière sûre et prévisible dans le flux XML. Donc, si le lecteur est en train d’indexer l’élément <Account>
, après l’analyse, le lecteur indexera la balise de fermeture </Accounts>
.
Le code d'analyse ressemble à ceci:
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 classe Statements
ne lit que dans le noeud <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;
}
}
}
}
La classe Statement
se ressemblerait beaucoup
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
}
}
}
Pour les sous-objets, ReadSubtree()
vous donne un lecteur xml limité aux sous-objets, mais je vraiment pense que vous le faites à la dure. Sauf si vous avez très spécifique exigences relatives à la gestion des fichiers XML inhabituels/imprévisibles, utilisez XmlSerializer
(éventuellement associé à sgen.exe
si vous le souhaitez vraiment).
XmlReader
est ... difficile. Contraste à:
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 {}}}}
});
}
}
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();
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();
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");
Vous pouvez parcourir xmlnode et obtenir les données ...... Lecteur C # XML
Je ne suis pas expérimenté. Mais je pense que XmlReader est inutile. C'est très difficile à utiliser.
XElement est très facile à utiliser.
Si vous avez besoin de performances (plus rapide), vous devez changer le format de fichier et utiliser les classes StreamReader et StreamWriter.