Schematron 中的 XPath:如何确定节点上是否存在 xmlns 属性

XPath in Schematron: How to determine if an xmlns attribute is present on a node

我正在尝试匹配缺少 xmlns 属性的特定元素的任何实例,但我无法通过语法进行匹配。我的xml如图:

<root>
<node xmlns:m="http://google.com"/>
<node style="block"/>
</root>

我想要 return 第一个节点,而不是第二个。如果我根据第二个节点上显示的 style 属性进行匹配,我可以简单地使用 not(@style) 但这不起作用不是(@xmlns:m)。我试图通过搜索具有与 URI 匹配的值的任何属性来规避此问题,但同样,这适用于其他属性,但不适用于 xmlns:m。 match/parse xmlns 属性与 XPath 是否有某种限制或语法怪癖?

Is there some sort of limitation or syntax quirk that's required to match/parse xmlns attributes with XPath?

是的,有点。怪癖是

xmlns:m="..."

语法上 属性,但比属性起着更具体的作用。它们是将前缀绑定到命名空间 URI 的 命名空间声明 。然后可以使用前缀来限定元素和属性名称。还有一个 默认命名空间 未绑定到前缀。

无法检测名称空间声明,因为 XPath(以及 XSLT 和 Schematron)不对实际 XML 文档进行操作,而是对它们的抽象表示进行操作。在这个表示中(a model),命名空间声明不存在,但是有命名空间 nodes 间接指向命名空间声明。

一旦 XML 解析器处理了 XML 文档,名称空间和属性就是您可以使用 XPath 轴 访问的不同类型的节点。我不确定我是否理解您为什么要这样做,但是您可以使用 namespace:: 轴报告命名空间节点:

namespace::*[not(. = 'http://www.w3.org/XML/1998/namespace')]

您必须小心并排除预定义的命名空间 URI

http://www.w3.org/XML/1998/namespace

默认绑定到xml:前缀。

ISO Schematron

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">

    <sch:pattern>
        <sch:rule context="node">
            <sch:report test="namespace::*[not(. = 'http://www.w3.org/XML/1998/namespace')]">Namespace node found!</sch:report>
        </sch:rule>
    </sch:pattern>

</sch:schema>

您显示的文档对该 SCH 文件无效,Schematron 验证器将指向具有命名空间声明的 node 元素:

<node xmlns:m="http://google.com"/>

作为错误的来源。


请注意

namespace::* 轴选择命名空间 节点 ,而不是命名空间声明。由于名称空间由范围内的所有元素继承,因此不仅声明名称空间的元素具有名称空间节点。它的所有后代也将有一个命名空间节点:

<root>
  <node xmlns:m="http://google.com">
    <descendant_element_with_namespace_node/>
  </node>
  <node style="block"/>
</root>

请参阅 LarsH 的回答以获取解释这一事实的更复杂的 XPath 表达式。

如其他地方所述,该问题要求的是 XPath 和 XML 工具一般不设计用来做的事情:提取有关命名空间 声明 的信息。 XPath 旨在能够可靠地检测 命名空间 (由其命名空间 URI,而不是其前缀标识)任何元素或属性,并根据其命名空间 select 节点。因此,任何使用标准 XML 工具检测命名空间声明的方法都注定是不可靠的。

基于 Mathias 的回答,我会说使用这个 XPath 测试:

namespace::*[not(. = 'http://www.w3.org/XML/1998/namespace')
         and not(. = ../../namespace::*)]

(使用 http://www.qutoric.com/xslt/analyser/xpathtool.html 测试)。在

这样的情况下
<root>
  <node xmlns:m="http://google.com">
    <node style="block"/>
  </node>
</root>

上面的 XPath 表达式只有一个 node 元素是真实的,即外部元素,因此满足了 OP 的问题;而 Mathias 的表达式对于 node 个元素都是真实的。

它通过测试命名空间 URI 未被父元素的命名空间节点共享的命名空间节点(在当前元素上)来工作。

但是,此 XPath 表达式也不会始终检测名称空间声明。例如,在

<root>
  <node xmlns:m="http://google.com">
    <node xmlns:g="http://google.com" style="block"/>
  </node>
</root>  

上面的 XPath 表达式只对外部 node 是真实的,并且不会检测内部的命名空间声明。同样,这是因为命名空间声明只是作为一种更容易指定哪些元素和属性在哪些命名空间中的方法,而不是作为重要的信息载体本身。

当然,上面的例子似乎不切实际,因为内部命名空间声明是多余的。尽管如此,它是格式良好的 XML,并且可以很容易地由行为良好的程序生成,这些程序生成内部 <node> 而无需直接了解外部 <node> 的命名空间声明。

附加警告:namespace:: 轴在 XPath 2.0 及更高版本中已弃用,因此您用于 运行 Schematron 的任何引擎都可能不支持它。