在我的应用程序中,我对XML字符串有一个已知的偏移,并想回答诸如“我的父元素是什么?”之类的问题。没有解析整个文档。
这个article提到了一个似乎在Objective-C中用于“向后”XML解析的库。我的应用程序不需要完整的XML支持,所以我很乐意忍受所有关于无法完全解析的警告。 C#/ .NET有这样的东西吗?
澄清:我一般都不会要求解析解决方案或性能权衡,我对特定的情况感兴趣,因为在某些情况下,我正处于文本流的中间位置,只需了解一些有关本地结构的信息。想象一下我不想获得文档顶部的情况,因为访问具有非常高的延迟。
答案 0 :(得分:3)
如果不对文本的性质做出一些重要的假设,就不可能做到这一点。最值得注意的是,您必须假设它是格式良好的XML,并且它既不包含CDATA部分也不包含名称空间。
如果你从一个流中间的任何位置开始并且备份直到你看到一个看似是元素开头的东西,你就无法知道你正在查看的文本是元素的开头。它可能是CDATA。而且你不能告诉它不是CDATA,直到你回溯整个流来寻找<![CDATA[
并且还没有找到它。
命名空间也存在类似的问题。如果您找到类似<Foo
的开始标记,则在您一直回溯到文档的根元素并确定没有祖先元素之前,您无法确定Foo
是否在默认命名空间中有一个命名空间声明。如果找到<x:Foo
,则必须回溯,直到找到带有xmlns:x
声明的封闭元素。
如果您确定文本是格式良好的XML,它不包含CDATA,并且它对命名空间的使用是有限的(即,您可以通过查看其开头来判断元素所在的命名空间标签),那么你想要做的一些事情至少是可能的。
您可以备份到遇到的第一个开始标记,创建一个StreamReader
,其原点就是该位置,然后使用它创建一个XPathDocument
来设置处理文档片段。顺便提一下,请注意,您不能保证XPathDocument
在第一次使用时不会一直读到文本末尾,除非您再次了解文本的性质并且您知道匹配的结束标记将存在。
但是这不会处理你提到的具体情况,即找到父元素。要查找父元素,您需要找到一个匹配结束标记之前(当您向后移动时)的开始标记。这并不是非常困难 - 您找到的每个<
字符都将是开始标记,结束标记或空元素的开头,您可以将结束标记放在堆栈上当您找到匹配的开始标记时将其弹出。当您点击开始标记并且堆栈为空时,您就在父元素的开头。
但这也是一个可能导致你回溯到流的起源的过程,特别是在你正在寻找的XML是典型的愚蠢XML日志格式的简单案例中:
<log>
<entry>...</entry>
<entry>...</entry>
...无限重复
答案 1 :(得分:2)
听起来像XPathDocument
可能就是你想要的。此类提供XML文档的快速,只读,内存中表示形式。它不构建DOM并针对XPath查询进行了优化。
XPathDocument也可用于解析XML片段。为此,您必须从XmlReader
创建它,并将其一致性级别设置为片段。
以下示例代码首先从XML片段中选择一组XML节点,然后根据XPath表达式选择每个节点的父节点:
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;
class Program
{
static void Main(string[] args)
{
string xml = File.ReadAllText(@"C:\tmp\smplInput.xml");
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.ConformanceLevel = ConformanceLevel.Fragment;
using (TextReader textReader = new StringReader(xml))
{
using (XmlReader xmlReader = XmlReader.Create(textReader, xrs))
{
// Create a new XPathDocument
XPathDocument doc = new XPathDocument(xmlReader);
// Create navigator
XPathNavigator navigator = doc.CreateNavigator();
// Set up namespace manager for XPath
XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
ns.AddNamespace("w", "http://www.example.com/2010/");
// Select nodes
XPathNodeIterator users = navigator.Select("//w:user", ns);
while (users.MoveNext())
{
XPathNavigator user = users.Current;
XPathNavigator department = user.SelectSingleNode("parent::node()", ns);
Console.WriteLine(string.Format("User {0} is in department {1}",
user.GetAttribute("name", ns.DefaultNamespace),
department.GetAttribute("type", ns.DefaultNamespace)));
}
}
}
}
}
要尝试代码,您可以使用以下XML输入文档:
<?xml version="1.0" encoding="utf-8" ?>
<w:departments xmlns:w="http://www.example.com/2010/">
<w:department type="A">
<w:user name="w" />
<w:user name="x" />
<w:department type="B">
<w:user name="x" />
<w:user name="y" />
</w:department>
<w:department type="C">
<w:user name="x" />
<w:user name="y" />
<w:user name="z" />
</w:department>
</w:department>
<w:department type="D">
<w:user name="w" />
</w:department>
</w:departments>
答案 2 :(得分:1)
另一种方法是解析XML一次,然后生成XML索引,以便下次加载索引时不需要重复解析XML ...请参阅下面的文章
答案 3 :(得分:0)
来自xponentsoftware的CAX完全符合您的要求。