我正在构建一个在XHTML文档上执行xpath 1.0查询的工具。在查询中使用命名空间前缀的要求正在扼杀我。查询如下所示:
html/body/div[@class='contents']/div[@class='body']/
div[@class='pgdbbyauthor']/h2[a[@name][starts-with(.,'Quick')]]/
following-sibling::ul[1]/li/a
(全部在一条线上)
...这已经够糟了,除非因为它是xpath 1.0,我需要在每个QName上使用显式名称空间前缀,所以它看起来像这样:
ns1:html/ns1:body/ns1:div[@class='contents']/ns1:div[@class='body']/
ns1:div[@class='pgdbbyauthor']/ns1:h2[ns1:a[@name][starts-with(.,'Quick')]]/
following-sibling::ns1:ul[1]/ns1:li/ns1:a
要设置查询,我会执行以下操作:
var xpathDoc = new XPathDocument(new StringReader(theText));
var nav = xpathDoc.CreateNavigator();
var xmlns = new XmlNamespaceManager(nav.NameTable);
foreach (string prefix in xmlNamespaces.Keys)
xmlns.AddNamespace(prefix, xmlNamespaces[prefix]);
XPathNodeIterator selection = nav.Select(xpathExpression, xmlns);
但我想要的是xpathExpression使用隐式默认命名空间。
有没有办法让我在编写之后转换unadorned xpath表达式,为查询中的每个元素名称注入一个名称空间前缀?
我在想,两个斜杠之间的任何东西,我都可以在那里注入一个前缀。当然除了“parent ::”和“preceding-sibling ::”之类的轴名称。和通配符。这就是我所说的“finagle默认命名空间”。
这个黑客会起作用吗?
附录
这就是我的意思。假设我有一个xpath表达式,在将它传递给nav.Select()之前,我将其转换。像这样:
string FixupWithDefaultNamespace(string expr)
{
string s = expr;
s = Regex.Replace(s, "^(?!::)([^/:]+)(?=/)", "ns1:$1"); // beginning
s = Regex.Replace(s, "/([^/:]+)(?=/)", "/ns1:$1"); // stanza
s = Regex.Replace(s, "::([A-Za-z][^/:*]*)(?=/)", "::ns1:$1"); // axis specifier
s = Regex.Replace(s, "\\[([A-Za-z][^/:*\\(]*)(?=[\\[\\]])", "[ns1:$1"); // predicate
s = Regex.Replace(s, "/([A-Za-z][^/:]*)(?!<::)$", "/ns1:$1"); // end
s = Regex.Replace(s, "^([A-Za-z][^/:]*)$", "ns1:$1"); // edge case
s = Regex.Replace(s, "([-A-Za-z]+)\\(([^/:\\.,\\)]+)(?=[,\\)])", "$1(ns1:$2"); // xpath functions
return s;
}
这实际上适用于我尝试过的简单案例。要使用上面的示例 - 如果输入是第一个xpath表达式,我得到的输出是第二个,带有所有ns1
前缀。真正的问题是,由于xpath表达式变得更复杂,期望这种Regex.Replace方法能够工作是否无望?
答案 0 :(得分:2)
不,XPath W3C spec is explicit about this:
“节点测试中的QName已扩展 使用。扩展名称 来自的命名空间声明 表达背景。这是一样的 方式扩展是为元素类型完成的 start和end-tags中的名称除外 声明的默认命名空间 不使用xmlns:如果是QName 没有前缀,那么 namespace URI为null(这是 属性名称相同 扩展)。如果QName是错误的 有一个前缀,没有 名称空间声明 表达语境。“
任何试图动态“按摩”事先知道XPath表达式以使其在这种情况下成功的尝试通常会失败,这意味着一个人应该能够执行完整的解析XPath表达式并隔离所有不是轴,运算符或函数名称的元素名称 - 这不是我要求任何人做的事情。甚至可能非常难以正确找到每个位置步骤的开始("/"
运算符),因为字符串“/”可能是文字字符串表达式的一部分。
需要完整解析的(子)表达式的一个示例是:
<强> div div div
强>
如果XPath表达式会对它们施加一些限制,那么这种方法可能会取得一些有限的成功,但我再也不建议去做,因为没有人可以证明 RegExes在所有情况下都能正常工作。
答案 1 :(得分:2)
如果您知道只有一个命名空间(即XHTML命名空间)并将其定义为默认命名空间,那么您可以通过使用不支持命名空间的XmlTextReader处理它来作弊,如下所示:
XmlTextReader tr = new XmlTextReader(new StringReader(@"<html xmlns=""http://www.w3.org/1999/xhtml"">
<head>
<title>Test</title>
</head>
<body>
<h1>Example</h1>
</body>
</html>"));
tr.Namespaces = false;
XPathDocument doc = new XPathDocument(tr);
tr.Close();
Console.WriteLine(doc.CreateNavigator().SelectSingleNode("html/body/h1").Value);
这适用于我并输出“示例”,因此路径“html / body / h1”找到“h1”元素。 其他选项是首先通过一些样式表运行输入以去除名称空间,然后使用剥离的名称空间处理转换结果。
当然,您可以考虑不依赖Microsoft XPath 1.0实现,而是转向第三方XPath 2.0或XQuery 1.0实现,如Saxon或类似XQSharp。然后,您可以为XPath或XQuery表达式定义默认元素名称空间,并使用不带前缀的路径来选择XHTML名称空间中的元素。
答案 2 :(得分:1)
这是一个廉价且快速的黑客攻击,完全消除了默认命名空间:更改xmlns属性的名称。
e.g。如果您已经在字符串变量中获得了xml,请在实际创建XPathDocument之前执行此操作:
xml = xml.Replace(" xmlns="," xxxxx=");
(在我的机器上的93kb文件上花了0.00065秒。)
然后,您可以自由使用精彩的无前缀XPath查询。
答案 3 :(得分:0)
我一直在使用默认命名空间regex hack一段时间,它似乎工作正常。我在Stackoverflow上找到了原始版本并添加了一些修改:
s = Regex.Replace(s, "^(?!(::|([A-Za-z][-A-Za-z]+\\(.+\\))))([^/:]+)(?=/)", prefix + ":$1"); // beginning
s = Regex.Replace(s, "/([^\\.^@^/:\\*\\(]+)(?=[/\\[])", "/" + prefix + ":$1"); //segment with fixed attribute
s = Regex.Replace(s, "(child|descendant|ancestor|ancestor-or-self|descendant-or-self|self|parent|following|following-sibling|preceding|preceding-sibling)::((?!([\\w]*\\(\\)))[A-Za-z][^/:*]*)((?=/)|(?=\\b))", "$1::" + prefix + ":$2"); // axis specifier
s = Regex.Replace(s, "\\[([A-Za-z][^/:*\\(]*)(?=[\\[\\]])", "[" + prefix + ":$1"); // within predicate
s = Regex.Replace(s, "/([A-Za-z][^/:\\*\\(]*)(?!<::)$", "/" + prefix + ":$1"); // end
s = Regex.Replace(s, "^([A-Za-z][^/:]*)$", prefix + ":$1"); // edge case
s = Regex.Replace(s, "([A-Za-z][-A-Za-z]+)\\(([^\\.^@^/:\\.,\\(\\)]+)(?=[,\\)])", "$1(" + prefix + ":$2"); // xpath functions with fixed attributes