Разъяснение о методе 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