我无法弄清楚XPath本身是否应该受到指责,或者它是否是特定的XPath实现使得这一点变得如此困难。 SO问题 - How to change an an XML element in a namespace with MSDeploy Parameters.xml file? - 是我的灵感。
以下是无法运作的基本示例。
XML:
<spring>
<objects xmlns="http://www.springframework.net">
<object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
<!--configure for server-->
<property name="DefaultCulture" value="en" />
</object>
</objects>
</spring>
的XPath:
//spring/objects/object[@id='CultureResolver']/@type
XPath查询不返回任何内容而不是:
Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web
我愿意天真地希望以下方面有效。
修改后的XML:
<spring>
<spring:objects xmlns:spring="http://www.springframework.net">
<spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
<!--configure for server-->
<spring:property name="DefaultCulture" value="en" />
</spring:object>
</spring:objects>
</spring>
修改过的XPath查询:
//spring/spring:objects/spring:object[@id='CultureResolver']/@type
此查询引发我使用的the online tester中的错误:
ERROR - Failed to evaluate XPath expression: org.apache.xpath.domapi.XPathStylesheetDOM3Exception: Prefix must resolve to a namespace: spring
修改后的XML:
<spring xmlns="" xmlns:spring="http://www.springframework.net">
<spring:objects>
<spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
<!--configure for server-->
<spring:property name="DefaultCulture" value="en" />
</spring:object>
</spring:objects>
</spring>
修改过的XPath查询(与我期望的工作相同):
//spring/spring:objects/spring:object[@id='CultureResolver']/@type
为了增加一些混乱,我发现以下XPath查询适用于原始示例XML(在在线测试器XPath引擎中):
//spring/*[local-name() = 'objects' and namespace-uri() = 'http://www.springframework.net']/*[@id='CultureResolver' and local-name() = 'object' and namespace-uri() = 'http://www.springframework.net']/@type
由于命名空间和前缀之间的相互作用,这会让人感到困惑吗?似乎声明没有前缀的命名空间不仅包括该命名空间中的相关元素,还包括其所有子节点,因此将其描述为&#34;默认命名空间&#34; (如this answer中的相关问题)。声明带有前缀的命名空间甚至不包括该命名空间中的相关元素!
是否有一些理由将名称空间需要包含在XML文档的根元素中,而与特定的XPath实现无关?
我试图解决的问题涉及Microsoft Web Deploy(MSDeploy)使用的任何XPath引擎。
我也在使用this online XPath tester。
答案 0 :(得分:8)
一个有趣且问题很好的问题!据我所知,难点在于XPath引擎处理输入文档中的命名空间声明的方式。
简短回答
不,这种行为一般与XPath或XPath规范无关。这是由于个别实施。
规范说什么
就XML和XPath规范而言,名称空间可以在任何元素上声明,并且最外层(或“根”)元素没有什么特别之处。根元素上的命名空间声明就像任何其他声明一样。
当然还有规则。例如,前缀必须与使用其QName的元素上的名称空间URI相关联,或者与该元素(或该属性)的祖先相关联。因此,以下内容不是格式良好的XML:
<prefix:root>
<child xmlns:prefix="www.example.com"/>
</prefix:root>
第二个重要规则:默认命名空间只能应用于声明它的元素和所有后代元素。在以下文档中,root
元素根本没有名称空间:
<root>
<child xmlns="www.example.com">
<grandchild/>
</child>
</root>
我所说的规格是XML,XML Namespaces和Xpath规格。
您的XPath实施会发生什么
现在,如果针对XML文档计算XPath表达式,则此输入文档中存在的所有名称空间声明也必须明确地可用于(声明或“注册”)到XPath引擎。
XPath的一些实现通过简单地重新声明属于作为Xpath引擎输入的XML文档的元素或属性的范围内的所有命名空间声明来简化此操作(另请参阅{{3} })。
在您的情况下,似乎只考虑在最外层元素上做出的声明。这就是你上一篇XML文档的原因:
<spring xmlns="" xmlns:spring="http://www.springframework.net">
<spring:objects>
<spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
<!--configure for server-->
<spring:property name="DefaultCulture" value="en" />
</spring:object>
</spring:objects>
</spring>
有效 - 因为名称空间声明是在根元素上进行的,并且您从根元素执行XPath表达式。您可以省略默认命名空间的未声明,因为它没有任何效果。
最后,回答你的上一个问题:
是否有某些原因需要将名称空间包含在XML文档的根元素中,而不依赖于特定的XPath实现?
不,除了
之外,没有理由应该在根元素上使用名称空间声明如果您的XPath实现自动重新声明范围内的命名空间声明,您当然可以利用它,但有时也会让您感到困惑,正如您所注意到的那样。
答案 1 :(得分:2)
不,文档和XPath的命名空间定义是分开的。默认情况下,某些实现会自动注册当前上下文的空间定义。我认为这是一个错误,因为它使得XPath不明确。
让我们从一个简单的例子开始:
<foo:element xmlns:foo="urn:foo"/>
为名称空间foo
定义了别名/前缀urn:foo
。 XML解析器解析该解析并识别节点element
属于命名空间urn:foo
。出于调试原因,节点名称可以写为{urn:foo}element
。
如果您更改前缀,甚至删除它,则始终以相同的方式解析。请考虑以下示例:
<foo:element xmlns:foo="urn:foo"/>
<bar:element xmlns:bar="urn:foo"/>
<element xmlns="urn:foo"/>
前缀/别名仅对节点及其后代有效。任何后代都可以拥有自己的定义,可能会覆盖其祖先之一。
对于XPath,您可以定义自己的别名。您编写名称空间解析程序或在XPath引擎上注册它们。这实际上取决于实施。
这是一个小的PHP示例:
$dom = new DOMDocument();
$dom->loadXml('<foo:element xmlns:foo="urn:foo"/>');
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('alias', 'urn:foo');
var_dump($xpath->evaluate('name(/alias:element)'));
输出:
string(11) "foo:element"
您可以看到XPath的命名空间定义是独立的,与XML文档中定义的前缀无关。
在Javascript中,XPath与Document.evaluate()
一起使用。第三个参数是命名空间解析器。
var resolver = {
namespaces : {
'alias' : 'urn:foo'
},
lookupNamespaceURI : function(prefix) {
if (prefix == '') {
return null;
}
return this.namespaces[prefix] || null;
}
};
console.log(
document.evaluate(
'name(/alias:element)'
),
document,
resolver,
XPathResult.ANY_TYPE,
null
).stringValue
);
回到你的问题。您必须了解如何为命名空间注册/定义别名/前缀。之后,您可以在XPath表达式中使用它们。如果为命名空间spring
定义别名http://www.springframework.net"
,则以下XPath表达式应该起作用:
//spring/spring:objects/spring:object[@id='CultureResolver']/@type
答案 2 :(得分:1)
在&#34;什么不起作用&#34;,问题是<object>
及其后代位于http://www.springframework.net
命名空间,但XPath表达式要求{ {1}}没有名称空间。
我不能立即明白为什么&#34;我期待的工作&#34;不应该工作,因为<object>
和<objects>
都明确地在<object>
命名空间中,并且XPath表达式正确地限定了元素名称(假设任何代码都解析了{{1}前缀可以访问命名空间绑定)。
在&#34;什么是工作&#34;,再次,http://www.springframework.net
和spring
都明确地在<objects>
命名空间中,并且XPath表达式正确地限定了元素名称。
与我期待工作的差异&#34;是适用于<object>
的默认命名空间,显式绑定到没有命名空间;所以我只能用&#34;我期待工作&#34;来猜测,默认命名空间(适用于http://www.springframework.net
)绑定到你不知道的某个命名空间。我建议你检查<spring>
是否有效 - 我想这已经证明了问题。