Чтение Xml с XmlReader в C#

Я пытаюсь прочитать следующий документ Xml с такой скоростью, как я могу и позволять дополнительным классам справиться с чтением каждого sub блока.

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

Однако я пытаюсь использовать объект XmlReader прочитать каждый Отчет и впоследствии "StatementsAvailable". Вы предлагаете использовать XmlReader. Считать и проверить каждый элемент и обработать его?

Я думал о разделении моих классов для обработки каждого узла правильно. Таким образом, существует класс AccountBase, который принимает экземпляр XmlReader, который читает NameOfKin и несколько других свойств об учетной записи. Затем я желал, межпоел через Операторы, и позвольте другому классу заполнить себя об Операторе (и впоследствии добавьте его к IList).

К настоящему времени я имею "в классе" часть, сделанная путем выполнения XmlReader. ReadElementString (), но я не могу тренировка, как сказать указателю перемещаться в элемент StatementsAvailable и позволять мне выполнить итерации через них и позволить другому классу считать каждый из тех proeprties.

Легкие звуки!

90
задан mezoid 14 March 2010 в 09:15
поделиться

3 ответа

По моему опыту XmlReader , очень легко случайно прочитать слишком много.Я знаю, что вы сказали, что хотите прочитать его как можно быстрее, но вы пробовали вместо этого использовать модель DOM? Я обнаружил, что LINQ to XML значительно упрощает работу с XML.

Если ваш документ особенно велик, вы можете объединить XmlReader и LINQ to 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 (которые огромны) в другой формат раньше - он работает очень хорошо.

РЕДАКТИРОВАТЬ из 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;
}
155
ответ дан 24 November 2019 в 06:59
поделиться

Для подобъектов 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 {}}}}
        });
    }
}
6
ответ дан 24 November 2019 в 06:59
поделиться

Мы постоянно выполняем такой синтаксический анализ XML. Ключ определяет, где метод синтаксического анализа оставит читателя при выходе. Если вы всегда оставляете читателя на следующем элементе после элемента, который был прочитан первым, вы можете безопасно и предсказуемо читать в потоке XML. Таким образом, если читатель в настоящее время индексирует элемент , после синтаксического анализа он проиндексирует закрывающий тег .

Код синтаксического анализа выглядит примерно так:

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 только что читает в узле

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

Класс 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
        }       
    }
}
17
ответ дан 24 November 2019 в 06:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: