根据我调用 reader 方法的方式,C# XmlReader 读取 XML 错误且不同
C# XmlReader reads XML wrong and different based on how I invoke the reader's methods
所以我目前对 C# XmlReader
工作原理的理解是,当我将其包装在以下构造中时,它采用给定的 XML 文件并逐个节点地读取它:
using System.Xml;
using System;
using System.Diagnostics;
...
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
settings.IgnoreProcessingInstructions = true;
using (XmlReader reader = XmlReader.Create(path, settings))
{
while (reader.Read())
{
// All reader methods I call here will reference the current node
// until I move the pointer to some further node by calling methods like
// reader.Read(), reader.MoveToContent(), reader.MoveToElement() etc
}
}
为什么以下两个片段(在上述构造中)会产生两个截然不同的结果,即使它们都调用相同的方法?
I used this example file for testing.
Debug.WriteLine(new string(' ', reader.Depth * 2) + "<" + reader.NodeType.ToString() + "|" + reader.Name + ">" + reader.ReadString() + "</>");
(片段 1)
对比
(片段 2)
string xmlcontent = reader.ReadString();
string xmlname = reader.Name.ToString();
string xmltype = reader.NodeType.ToString();
int xmldepth = reader.Depth;
Debug.WriteLine(new string(' ', xmldepth * 2) + "<" + xmltype + "|" + xmlname + ">" + xmlcontent + "</>");
代码段 1 的输出:
<XmlDeclaration|xml></>
<Element|rss></>
<Element|head></>
<Text|>Test Xml File</>
<Element|description>This will test my xml reader</>
<EndElement|head></>
<Element|body></>
<Element|g:id>1QBX23</>
<Element|g:title>Example Title</>
<Element|g:description>Example Description</>
<EndElement|item></>
<Element|item></>
<Text|>2QXB32</>
<Element|g:title>Example Title</>
<Element|g:description>Example Description</>
<EndElement|item></>
<EndElement|body></>
<EndElement|xml></>
<EndElement|rss></>
是的,它的格式与我输出中的一样 window。可以看出,它跳过了某些元素并为其他一些元素输出了错误的深度。因此,NodeTypes 是正确的,与代码片段 2 不同,它输出:
<XmlDeclaration|xml></>
<Element|xml></>
<Element|title></>
<EndElement|title>Test Xml File</>
<EndElement|description>This will test my xml reader</>
<EndElement|head></>
<Element|item></>
<EndElement|g:id>1QBX23</>
<EndElement|g:title>Example Title</>
<EndElement|g:description>Example Description</>
<EndElement|item></>
<Element|g:id></>
<EndElement|g:id>2QXB32</>
<EndElement|g:title>Example Title</>
<EndElement|g:description>Example Description</>
<EndElement|item></>
<EndElement|body></>
<EndElement|xml></>
<EndElement|rss></>
再一次,深度搞砸了,但它不像代码片段 1 那样严重。它还跳过了一些元素并分配了错误的节点类型。
为什么不能输出预期的结果?为什么这两个片段会产生两个完全不同的输出,具有不同的深度、NodeTypes 和跳过的节点?[=35=]
我很感激这方面的任何帮助。我搜索了很多关于此的任何答案,但似乎我是唯一遇到这些问题的人。我在 Visual Studio 2017 年将 .NET Framework 4.6.2 与 Asp.net Web Forms 一起使用。
首先,您使用的方法XmlReader.ReadString()
已弃用:
XmlReader.ReadString Method
... reads the contents of an element or text node as a string. However, we recommend that you use the ReadElementContentAsString
method instead, because it provides a more straightforward way to handle this operation.
但是,除了警告我们关闭该方法之外,该文档并未准确说明它的实际作用。要确定这一点,我们需要转到 reference source:
public virtual string ReadString() {
if (this.ReadState != ReadState.Interactive) {
return string.Empty;
}
this.MoveToElement();
if (this.NodeType == XmlNodeType.Element) {
if (this.IsEmptyElement) {
return string.Empty;
}
else if (!this.Read()) {
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
}
if (this.NodeType == XmlNodeType.EndElement) {
return string.Empty;
}
}
string result = string.Empty;
while (IsTextualNode(this.NodeType)) {
result += this.Value;
if (!this.Read()) {
break;
}
}
return result;
}
此方法执行以下操作:
若当前节点为空元素节点,return空字符串
如果当前节点是非空元素,前进reader。
如果当前节点是元素的末尾,return 一个空字符串。
当当前节点是文本节点时,将文本添加到字符串中并推进reader。只要当前节点不是文本节点,return 累积的字符串。
由此可见,这个方法是为了推进reader而设计的。我们还可以看到,给定混合内容 XML,如 <head>text <b>BOLD</b> more text</head>
,ReadString()
将仅部分读取 <head>
元素,而 reader 位于 <b>
。这种奇怪的现象可能是 Microsoft 弃用该方法的原因。
我们还可以了解为什么您的两个代码段的功能不同。首先,在调用 ReadString()
并推进 reader 之前,您会得到 reader.Depth
和 reader.NodeType
。在推进 reader.
之后,您将获得这些属性。
由于您的目的是遍历节点并获取每个节点的值,而不是 ReadString()
或 ReadElementContentAsString()
,您应该只使用 XmlReader.Value
:
gets the text value of the current node.
因此,您更正后的代码应如下所示:
string xmlcontent = reader.Value;
string xmlname = reader.Name.ToString();
string xmltype = reader.NodeType.ToString();
int xmldepth = reader.Depth;
Console.WriteLine(new string(' ', xmldepth * 2) + "<" + xmltype + "|" + xmlname + ">" + xmlcontent + "</>");
XmlReader
很难使用。您始终需要检查文档以确定给定方法将 reader 定位的确切位置。例如,XmlReader.ReadElementContentAsString()
moves the reader past the end of the element, whereas XmlReader.ReadSubtree()
将 reader 移动到 元素的末尾。但作为一般规则,任何名为 Read
的方法都会推进 reader,因此在外部 while (reader.Read())
循环中使用 Read
方法时需要小心。
演示 fiddle here.
所以我目前对 C# XmlReader
工作原理的理解是,当我将其包装在以下构造中时,它采用给定的 XML 文件并逐个节点地读取它:
using System.Xml;
using System;
using System.Diagnostics;
...
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
settings.IgnoreProcessingInstructions = true;
using (XmlReader reader = XmlReader.Create(path, settings))
{
while (reader.Read())
{
// All reader methods I call here will reference the current node
// until I move the pointer to some further node by calling methods like
// reader.Read(), reader.MoveToContent(), reader.MoveToElement() etc
}
}
为什么以下两个片段(在上述构造中)会产生两个截然不同的结果,即使它们都调用相同的方法?
I used this example file for testing.
Debug.WriteLine(new string(' ', reader.Depth * 2) + "<" + reader.NodeType.ToString() + "|" + reader.Name + ">" + reader.ReadString() + "</>");
(片段 1)
对比
(片段 2)
string xmlcontent = reader.ReadString();
string xmlname = reader.Name.ToString();
string xmltype = reader.NodeType.ToString();
int xmldepth = reader.Depth;
Debug.WriteLine(new string(' ', xmldepth * 2) + "<" + xmltype + "|" + xmlname + ">" + xmlcontent + "</>");
代码段 1 的输出:
<XmlDeclaration|xml></>
<Element|rss></>
<Element|head></>
<Text|>Test Xml File</>
<Element|description>This will test my xml reader</>
<EndElement|head></>
<Element|body></>
<Element|g:id>1QBX23</>
<Element|g:title>Example Title</>
<Element|g:description>Example Description</>
<EndElement|item></>
<Element|item></>
<Text|>2QXB32</>
<Element|g:title>Example Title</>
<Element|g:description>Example Description</>
<EndElement|item></>
<EndElement|body></>
<EndElement|xml></>
<EndElement|rss></>
是的,它的格式与我输出中的一样 window。可以看出,它跳过了某些元素并为其他一些元素输出了错误的深度。因此,NodeTypes 是正确的,与代码片段 2 不同,它输出:
<XmlDeclaration|xml></>
<Element|xml></>
<Element|title></>
<EndElement|title>Test Xml File</>
<EndElement|description>This will test my xml reader</>
<EndElement|head></>
<Element|item></>
<EndElement|g:id>1QBX23</>
<EndElement|g:title>Example Title</>
<EndElement|g:description>Example Description</>
<EndElement|item></>
<Element|g:id></>
<EndElement|g:id>2QXB32</>
<EndElement|g:title>Example Title</>
<EndElement|g:description>Example Description</>
<EndElement|item></>
<EndElement|body></>
<EndElement|xml></>
<EndElement|rss></>
再一次,深度搞砸了,但它不像代码片段 1 那样严重。它还跳过了一些元素并分配了错误的节点类型。
为什么不能输出预期的结果?为什么这两个片段会产生两个完全不同的输出,具有不同的深度、NodeTypes 和跳过的节点?[=35=] 我很感激这方面的任何帮助。我搜索了很多关于此的任何答案,但似乎我是唯一遇到这些问题的人。我在 Visual Studio 2017 年将 .NET Framework 4.6.2 与 Asp.net Web Forms 一起使用。
首先,您使用的方法XmlReader.ReadString()
已弃用:
XmlReader.ReadString Method
... reads the contents of an element or text node as a string. However, we recommend that you use the
ReadElementContentAsString
method instead, because it provides a more straightforward way to handle this operation.
但是,除了警告我们关闭该方法之外,该文档并未准确说明它的实际作用。要确定这一点,我们需要转到 reference source:
public virtual string ReadString() {
if (this.ReadState != ReadState.Interactive) {
return string.Empty;
}
this.MoveToElement();
if (this.NodeType == XmlNodeType.Element) {
if (this.IsEmptyElement) {
return string.Empty;
}
else if (!this.Read()) {
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
}
if (this.NodeType == XmlNodeType.EndElement) {
return string.Empty;
}
}
string result = string.Empty;
while (IsTextualNode(this.NodeType)) {
result += this.Value;
if (!this.Read()) {
break;
}
}
return result;
}
此方法执行以下操作:
若当前节点为空元素节点,return空字符串
如果当前节点是非空元素,前进reader。
如果当前节点是元素的末尾,return 一个空字符串。
当当前节点是文本节点时,将文本添加到字符串中并推进reader。只要当前节点不是文本节点,return 累积的字符串。
由此可见,这个方法是为了推进reader而设计的。我们还可以看到,给定混合内容 XML,如 <head>text <b>BOLD</b> more text</head>
,ReadString()
将仅部分读取 <head>
元素,而 reader 位于 <b>
。这种奇怪的现象可能是 Microsoft 弃用该方法的原因。
我们还可以了解为什么您的两个代码段的功能不同。首先,在调用 ReadString()
并推进 reader 之前,您会得到 reader.Depth
和 reader.NodeType
。在推进 reader.
由于您的目的是遍历节点并获取每个节点的值,而不是 ReadString()
或 ReadElementContentAsString()
,您应该只使用 XmlReader.Value
:
gets the text value of the current node.
因此,您更正后的代码应如下所示:
string xmlcontent = reader.Value;
string xmlname = reader.Name.ToString();
string xmltype = reader.NodeType.ToString();
int xmldepth = reader.Depth;
Console.WriteLine(new string(' ', xmldepth * 2) + "<" + xmltype + "|" + xmlname + ">" + xmlcontent + "</>");
XmlReader
很难使用。您始终需要检查文档以确定给定方法将 reader 定位的确切位置。例如,XmlReader.ReadElementContentAsString()
moves the reader past the end of the element, whereas XmlReader.ReadSubtree()
将 reader 移动到 元素的末尾。但作为一般规则,任何名为 Read
的方法都会推进 reader,因此在外部 while (reader.Read())
循环中使用 Read
方法时需要小心。
演示 fiddle here.