Чтение Xml с помощью XmlReader в C#
Я пытаюсь прочитать следующий Xml-документ так быстро, как могу, и позволить дополнительным классам управлять чтением каждого подблока.
<ApplicationPool>
<Accounts>
<Account>
<NameOfKin></NameOfKin>
<StatementsAvailable>
<Statement></Statement>
</StatementsAvailable>
</Account>
</Accounts>
</ApplicationPool>
тем не менее, я пытаюсь использовать объект XmlReader для чтения каждой учетной записи, а затем "StatementsAvailable". Вы предлагаете использовать XmlReader.Читать и проверять каждый элемент и обрабатывать его?
Я думал о разделении моих классов для правильной обработки каждого узла. Итак, есть класс AccountBase, который принимает XmlReader экземпляр, который читает NameOfKin и несколько других свойств об учетной записи. Затем я хотел взаимодействовать через операторы и позволить другому классу заполнить себя о заявлении (а затем добавить его в IList).
до сих пор у меня есть часть" за класс", выполненная с помощью XmlReader.ReadElementString() но я не могу тренироваться, как сказать указателю перейти к элементу StatementsAvailable и позволить мне перебирать их и позволить другому классу читать каждый из них proeprties.
звуки легко!
7 ответов:
мой опыт
XmlReader- Это очень легко случайно прочитал слишком много. Я знаю, вы сказали, что хотите прочитать его как можно быстрее, но вы пробовал используя модель DOM вместо этого? Я обнаружил, что LINQ to XML заставляет XML работать намного много легче.если ваш документ особенно огромен, вы можете объединить
XmlReaderи LINQ в XML, создавXElementСXmlReaderдля каждого из ваших "внешних" элементов в потоковом режиме: это позволяет вы выполняете большую часть работы по преобразованию в LINQ to 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; } } } } } }я использовал это для преобразования пользовательских данных StackOverflow (который огромен) в другой формат, прежде чем - он работает очень хорошо.
EDIT from radarbob, переформатированный Джоном - хотя не совсем ясно, какая проблема" читать слишком далеко " упоминается к...
это должно упростить вложенность и позаботиться о проблеме "слишком далеко читать".
using (XmlReader reader = XmlReader.Create(inputUrl)) { reader.ReadStartElement("theRootElement"); while (reader.Name == "TheNodeIWant") { XElement el = (XElement) XNode.ReadFrom(reader); } reader.ReadEndElement(); }это заботится о проблеме" слишком далеко читать", потому что он реализует классический шаблон цикла while:
initial read; (while "we're not at the end") { do stuff; read; }
три года спустя, возможно, с новым акцентом на данные WebApi и xml, я столкнулся с этим вопросом. Поскольку codewise я склонен следовать за скитом из самолета без парашюта, и видя его начальный код дважды corraborated статьей команды MS Xml, а также примером в BOL потоковое преобразование больших Xml-документов, Я очень быстро пропустил другие комментарии, в частности, от "pbz", который указал, что если у вас есть те же элементы по имени в последовательность, каждый другой пропускается из-за двойного чтения. И на самом деле, статьи в блоге BOL и MS анализировали исходные документы с целевыми элементами, вложенными глубже, чем второй уровень, маскируя этот побочный эффект.
другие ответы решают эту проблему. Я просто хотел предложить немного более простую версию, которая, похоже, хорошо работает до сих пор, и учитывает, что xml может поступать из разных источников, а не только из uri, и поэтому расширение работает на управляемом пользователем XmlReader. Одно предположение состоит в том, что читатель находится в своем начальном состоянии, так как в противном случае первый "Read ()" может пройти мимо желаемого узла:
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(); } }
мы делаем этот вид XML-разбора все время. Ключ определяет, где метод разбора оставит читателя на выходе. Если вы всегда оставляете средство чтения на следующем элементе после элемента, который был прочитан первым, то вы можете безопасно и предсказуемо читать в потоке XML. Так что если читатель в настоящее время индексирует
<Account>элемент, после разбора читатель будет индексировать</Accounts>закрывающий тег.синтаксический код выглядит примерно так:
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; } } } }в
Statementsкласс просто читает в<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; } } } }The
Statementкласс будет выглядеть очень похоже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 } } }
для подобъектов,
ReadSubtree()дает вам xml-читатель, ограниченный подобъектами, но я действительно думаю, что вы делаете это трудный путь. Если у вас нет очень специфический требования к обработке необычных / непредсказуемых xml, используйтеXmlSerializer(возможно, в сочетании сsgen.exeЕсли вы действительно хотите).
XmlReaderесть... хитрый. Контраст к: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();
У меня нет опыта .Но я думаю, что XmlReader не нужен. Это очень трудно использовать.
XElement очень прост в использовании.
Если вам нужна производительность (быстрее), вы должны изменить формат файла и использовать классы StreamReader и 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");вы можете перебрать xmlnode и получить данные...... C# XML Reader
Comments