是否需要在根元素中声明 XML 命名空间才能被 XPath 查询匹配? [英] Do XML namespaces need to be declared in the root element to be matchable by an XPath query?

查看:21
本文介绍了是否需要在根元素中声明 XML 命名空间才能被 XPath 查询匹配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道是否应该归咎于 XPath 本身,或者是否是特定的 XPath 实现使这变得如此困难.SO问题 - 如何使用 MSDeploy Parameters.xml 文件更改命名空间中的 XML 元素?——这是我的灵感.

什么不起作用

这是不起作用的基本示例.

XML:

<对象 xmlns="http://www.springframework.net"><object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web"><!--为服务器配置-->

XPath:

//spring/objects/object[@id='CultureResolver']/@type

XPath 查询不返回任何内容,而是:

Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web

我期望的工作

也许天真地,我希望以下方法能够奏效.

修改后的 XML:

<spring:objects xmlns:spring="http://www.springframework.net"><spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web"><!--为服务器配置--><spring:property name="DefaultCulture" value="en"/></spring:对象></spring:对象></弹簧>

修改后的 XPath 查询:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

此查询在我使用的在线测试仪中引发错误:

错误 - 无法评估 XPath 表达式:org.apache.xpath.domapi.XPathStylesheetDOM3Exception:前缀必须解析为命名空间:spring

什么工作

修改后的 XML:

<spring xmlns=""xmlns:spring="http://www.springframework.net"><弹簧:对象><spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web"><!--为服务器配置--><spring:property name="DefaultCulture" value="en"/></spring:对象></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' andlocal-name() = 'object' 和 namespace-uri() = 'http://www.springframework.net']/@type

为什么?

这是因为命名空间和前缀之间的相互作用而令人困惑吗?似乎声明一个没有前缀的命名空间不仅包括该命名空间中的相关元素,还包括其所有子元素,因此将其描述为默认命名空间"(如 this answer 相关问题).而使用前缀声明命名空间甚至不包括该命名空间中的相关元素!

命名空间需要包含在 XML 文档的根元素中,与特定的 XPath 实现无关,有什么原因吗?

我的 XPath 引擎

我试图解决的问题涉及 Microsoft Web Deploy (MSDeploy) 使用的任何 XPath 引擎.

我也在使用 这个在线 XPath 测试器.

解决方案

一个有趣的问题!据我所知,困难在于您的 XPath 引擎处理输入文档中的命名空间声明的方式.

简短回答

不,这种行为通常与 XPath 或 XPath 规范无关.这是由于个别实施.

<小时>

规格说明

就 XML 和 XPath 规范而言,可以在任何元素上声明名称空间,最外层(或根")元素没有什么特别之处.根元素上的命名空间声明与任何其他声明一样.

当然还是有规则的.例如,前缀必须与使用其 QName 的元素或该元素(或该属性)的祖先的名称空间 URI 相关联.因此,以下不是格式良好的 XML:

<子 xmlns:prefix="www.example.com"/></前缀:root>

第二个重要规则:默认命名空间只能应用于声明它的元素和所有后代元素.在以下文档中,root 元素根本不在命名空间中:

<child xmlns="www.example.com"><孙子/></孩子></root>

我所说的规范是 XML, XML 命名空间Xpath 规范.

XPath 的实现会发生什么

现在,如果针对 XML 文档评估 XPath 表达式,则此输入文档中存在的所有命名空间声明也必须显式地提供给 XPath 引擎(声明或注册").

XPath 的一些实现通过简单地重新声明在用作 Xpath 引擎输入的 XML 文档的元素或属性范围内的所有命名空间声明来简化这一点(另请参阅 这个).

在您的情况下,似乎只考虑在最外层元素上进行的声明.这就是您上一个 XML 文档的原因:

<spring xmlns=""xmlns:spring="http://www.springframework.net"><弹簧:对象><spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web"><!--为服务器配置--><spring:property name="DefaultCulture" value="en"/></spring:对象></spring:对象></弹簧>

有效 - 因为命名空间声明是在根元素上进行的,并且您从根元素执行 XPath 表达式.不过,您可以省略默认命名空间的取消声明,因为它没有任何效果.

<小时>

最后,回答你的最后一个问题:

<块引用>

有什么理由需要在 XML 文档的根元素中包含命名空间,而不依赖于特定的 XPath 实现?

不,没有理由将命名空间声明放在根元素上,除了

  • 在我看来,在根元素上声明时更容易找到它们(非常主观)
  • 如果您想为整个文档声明一个默认命名空间.在根元素上声明它是让它也适用于根元素的唯一方法
  • 如果根元素本身有一个限定名称,即带有前缀.然后,您必须在根元素上声明此前缀和命名空间 URI.

如果您的 XPath 实现自动重新声明范围内的命名空间声明,您当然可以利用它,但正如您所注意到的,它有时也会令人困惑.

I can't figure out whether XPath itself is to blame or whether it's the particular XPath implementations that make this so difficult. The SO question – How to change an an XML element in a namespace with MSDeploy Parameters.xml file? – was my inspiration.

What Doesn't Work

Here's the basic example that doesn't work.

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

The XPath query returns nothing instead of:

Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web

What I Expect to Work

I would, perhaps naively, expect the following to work.

Modified 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>

Modified XPath query:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

This query raises an error in the online tester I used:

ERROR - Failed to evaluate XPath expression: org.apache.xpath.domapi.XPathStylesheetDOM3Exception: Prefix must resolve to a namespace: spring

What Does Work

Modified 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>

Modified XPath query (same as under What I Expect to Work):

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

To add to the confusion somewhat, I discovered that the following XPath query works for the original example XML (in the online tester XPath engine):

//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

Why?

Is this confusing because of the interplay between namespaces and prefixes? It seems like declaring a namespace without a prefix not only includes the relevant element in that namespace but also includes all of its children too, hence describing it as a "default namespace" (like in this answer to a related question). Whereas declaring a namespace with a prefix doesn't even include the relevant element in that namespace!

Is there some reason why namespaces need to be included in the root element of the XML document, independent of particular XPath implementations?

My XPath Engines

The problem I was trying to solve involved whatever XPath engine that Microsoft Web Deploy (MSDeploy) uses.

I was also using this online XPath tester.

解决方案

An interesting and well-asked question! As far as I can see, the difficulty lies with the way your XPath engine handles namespace declarations found in the input document.

The short answer

No, this behaviour has nothing to do with XPath in general or with the XPath specification. It is due to individual implementations.


What the specifications say

As far as the XML and XPath specifications are concerned, namespaces can be declared on any element, and there is nothing special about the outermost (or "root") element. Namespace declarations on the root element are just like any other declaration.

Of course there are still rules. For instance, a prefix must be associated with a namespace URI on the element in whose QName it is used, or on an ancestor of that element (or of that attribute). So, the following is not well-formed XML:

<prefix:root>
    <child xmlns:prefix="www.example.com"/>
</prefix:root>

And the second important rule: A default namespace can only apply to the element on which it is declared and all descendant elements. In the following document, the root element is in no namespace at all:

<root>
   <child xmlns="www.example.com">
      <grandchild/>
   </child>
</root>

The specifications I am talking about are the XML, XML Namespaces and Xpath specifications.

What happens in your implementation of XPath

Now, if an XPath expression is evaluated against an XML document, all namespace declarations present in this input document must also explicitly be made available (declared, or "registered") to the XPath engine.

Some implementations of XPath simplify this by simply redeclaring all namespace declarations that are in scope for an element or attribute of an XML document that serves as input for an Xpath engine (also see this).

In your case, it seems only declarations made on the outermost element are considered. That's why your last XML document:

<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>

works - because the namespace declaration is made on the root element and you execute the XPath expression from the root element. You could omit the undeclaration of a default namespace though, because it doesn't have any effect.


Finally, to answer your last question:

Is there some reason why namespaces need to be included in the root element of the XML document, independent of particular XPath implementations?

No, there is no reason namespace declarations should be on the root element, except

  • that they are somewhat easier to find when declared on the root element, in my opinion (highly subjective)
  • if you'd like to declare a default namespace for the whole document. Declaring it on the root element is the only way to have it also apply to the root element
  • if the root element itself has a qualified name, i.e. is prefixed. Then, you must declare this prefix and namespace URI on the root element.

If your implementation of XPath automatically redeclares namespace declarations that are in scope, you can of course take advantage of it, but it will also be confusing sometimes, as you have noticed.

这篇关于是否需要在根元素中声明 XML 命名空间才能被 XPath 查询匹配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆