试图理解XmlReader.Read()
和XmlReader.ReadStartElement()
之间的"不一致"。在下面的reader1
中,一切都是意料之中的,即需要3次读取才能读取整个xml;更重要的是,当第一次读取即读取<firstname>
时,reader1.Value
为空。第二个读数reader1.Value
是文本节点值。
但在reader2
中,我期望相同的读取顺序,因为据我所知,ReadStartElement()
内部调用Read()
,它应该只读取一个XmlNodeType
,例如这里的<firstname>
。这几乎就像我们可以用一个调用来替换ReadStartElement("firstname")
,以检查它是否是名为firstname
的起始元素和对Read()
的调用。为什么reader2.Value
在ReadStartElement("firstname")
之后不为空?我最初是在@lesscode的问题下问这个问题的,他的解释是,根据msdn,ReadStartElement()
将把XmlReader
提前到下一个节点,reader.Value
是当前节点的值。但如果是这样的话,Read()
和ReadStartElement()
之间不是不一致吗?因为使用Read()
之后必须检索Value,而使用ReadStartElement()
之前必须检索Value
。
var simpleElement = "<firstname>Jim</firstname>";
using (var reader1 = XmlReader.Create(new StringReader(simpleElement)))
{
var i = 1;
while (reader1.Read())
{
WriteLine($"i = {i++}; value = {reader1.Value}");
}
}
using (var reader2 = XmlReader.Create(new StringReader(simpleElement)))
{
// this internally calls Read() which should have ONLY read the 'firstname' start element node.
reader2.ReadStartElement("firstname");
// prints Jim; but why??? The text node has NOT been read yet!
WriteLine(reader2.Value);
reader2.Read(); //WHY needs this line given text node has been read already?
reader2.ReadEndElement();
}
您可以查看Github上的源代码:XmlReader.cs.
正如您在下面看到的,这些方法的行为不同:
// Checks that the current node is an element and advances the reader to the next node.
public virtual void ReadStartElement() {
if (MoveToContent() != XmlNodeType.Element) {
throw new XmlException(Res.Xml_InvalidNodeType, this.NodeType.ToString(), this as IXmlLineInfo);
}
Read();
}
// Checks whether the current node is a content (non-whitespace text, CDATA, Element, EndElement, EntityReference
// or EndEntity) node. If the node is not a content node, then the method skips ahead to the next content node or
// end of file. Skips over nodes of type ProcessingInstruction, DocumentType, Comment, Whitespace and SignificantWhitespace.
public virtual XmlNodeType MoveToContent() {
do {
switch (this.NodeType) {
case XmlNodeType.Attribute:
MoveToElement();
goto case XmlNodeType.Element;
case XmlNodeType.Element:
case XmlNodeType.EndElement:
case XmlNodeType.CDATA:
case XmlNodeType.Text:
case XmlNodeType.EntityReference:
case XmlNodeType.EndEntity:
return this.NodeType;
}
} while (Read());
return this.NodeType;
}
因此,ReadStartElement
方法,调用MoveToContent
,可以进行多次Read
调用,以便找到内容节点。此后,ReadStartElement
读取当前启动元件。