我正在尝试用C#解析一个非常大的XML文件 - 足够大,以至于某些XML工具无法处理它,所以我想按顺序处理它而不是全部加载它。另外,如果有某些错误在源代码中,我希望能够报告错误以及发生错误的XML中的行号。
不幸的是,XML在不同级别重复元素名称,如:
<foo>
<foo>
<foo>Something interesting</foo>
</foo>
Something else interesting
<foo>Yes, it's horrid, isn't it?</foo>
</foo>
我需要跟踪事情发生的嵌套级别。
我尝试过使用XmlTextReader,但我似乎只获得了foo
个元素的列表:我无法弄清楚如何跟踪嵌套级别。我的下一个想法是在每个元素上使用ReadSubtree,所以当我从嵌套返回时,我可以使用它来告诉我。但是它返回一个XmlReader,而不是一个XmlTextReader,所以我不再能够访问原始XML的行号。一个网络搜索建议使用ReadOuterXml来获取节点的文本并从中生成另一个阅读器,但这似乎是在整个文本中读取的,所以我回到原来的文件问题那么大。
那么如何跟踪嵌套级别(当元素名称没有帮助时)和源行号而不加载整个文件?
答案 0 :(得分:2)
回答您的相关问题:
您可以cast your XmlReader
to an IXmlLineInfo
提取行号。请注意,并非所有XmlReader
实现都实现此接口,但XmlReader.Create Method(string inputUri)
返回的实现具有此接口。过时的XmlTextReader
也会。
要获取当前深度,请使用XmlReader.Depth
。
更一般地说,您可以在遍历文件时维护一堆XName
类,例如:
public static class XmlReaderExtensions
{
public static void WalkXmlNodes(this XmlReader xmlReader, Action<XmlReader, Stack<XName>, IXmlLineInfo> action)
{
IXmlLineInfo xmlInfo = xmlReader as IXmlLineInfo;
try
{
Stack<XName> names = new Stack<XName>();
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
names.Push(XName.Get(xmlReader.LocalName, xmlReader.NamespaceURI));
}
action(xmlReader, names, xmlInfo);
if ((xmlReader.NodeType == XmlNodeType.Element && xmlReader.IsEmptyElement)
|| xmlReader.NodeType == XmlNodeType.EndElement)
{
names.Pop();
}
}
}
catch (Exception ex)
{
// Rethrow exception with line number information.
var line = (xmlInfo == null ? -1 : xmlInfo.LineNumber);
var pos = (xmlInfo == null ? -1 : xmlInfo.LinePosition);
var xmlException = new XmlException("XmlException occurred", ex, line, pos);
throw xmlException;
}
}
}