Разъяснение о методе XmlReader.ReadSubtree

Я прочитал документацию по этому методу на mdsn, но не могу понять причину следующего поведения:

Предположим, есть этот XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<project>
    <caseInformation Name="1">
        <field >aaaaaaa</field>
        <field >bvbbbb</field>
        <field >cccc</field>
        <field >ddddd</field>
    </caseInformation>
</project>

Теперь, если вы запустите следующий фрагмент кода:

while (reader.Read())
{
    if (reader.NodeType == XmlNodeType.Element)
    {
        var name = reader.Name;
        if(name == "caseInformation")
        {
            using (var innerReader = reader.ReadSubtree())
            {
                while (innerReader.ReadToFollowing("field"))
                {

                    var element = (XElement)XNode.ReadFrom(innerReader);
                    element.Dump();
                }
            }
         }
     }
}

это даст следующий результат:

<field>aaaaaaa</field>

<field>bvbbbb</field>

<field>cccc</field>

<field>ddddd</field>

если я изменю способ получения элемента, используя эту инструкцию

var element = (XElement)XNode.ReadFrom(reader);

я получаю такой же вывод. Но я не могу понять почему.

На MSDN заявлено, что

ReadSubTree () возвращает новый экземпляр XmlReader, который можно использовать для чтения текущего узла и всех его потомков.

и это

Когда новое средство чтения XML было закрыто, исходное средство чтения располагается в узле EndElement поддерева

так это означает, что на самом деле, когда я перемещаю innerReader, также перемещается оригинальный читатель?

2 ответа

Решение

На самом деле новый XmlReader, возвращаемый методом ReadSubtree(), на самом деле является не новым XmlReader, а оболочкой для исходного XmlReader.

Вы можете видеть это в исходном коде фреймворка:

    public virtual  XmlReader  ReadSubtree() {
        if (NodeType != XmlNodeType.Element) {
            throw new InvalidOperationException(Res.GetString(Res.Xml_ReadSubtreeNotOnElement));
        }
        return new XmlSubtreeReader(this);
    }

это XmlSubtreeReader наследуется от XmlWrappingReader,

И если вы видите конструктор:

    internal XmlWrappingReader( XmlReader baseReader ) {
        Debug.Assert( baseReader != null );
        this.reader = baseReader;
        this.readerAsIXmlLineInfo = baseReader as IXmlLineInfo;
    }

так это означает, что на самом деле, когда я перемещаю innerReader, также перемещается оригинальный читатель?

Точно, в любом случае это не проблема, потому что вы не должны вызывать метод в исходном считывателе, пока "subReader" не будет закрыт, иначе вы получите исключение.

Пример использования метода на основе вашего примера кода:

    public void ReadProjectInfo()
    {
        //...

        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                if (reader.Name == "caseInformation")
                {
                    ReadCaseInformation(reader.ReadSubtree());
                }
            }
        }
    }

    public void ReadCaseInformation(XmlReader reader)
    {
        try
        {
            // if something raise an exception here
            // the main reader is not really affected by that
            // because when the methods ends, the "subReader" is closed
            // and then the main reader is set on the closing tag.
            // so the main XML reader can continue to analyze the xml stream
        }
        catch (Exception)
        {
            //...
        }
    }

Надеюсь это поможет

Заменять:

      var element = (XElement)XNode.ReadFrom(innerReader);

К:

      var element = innerReader.ReadElementContentAsString();

Выход:

      aaaaaaa
bvbbbb
cccc
ddddd
Другие вопросы по тегам