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

时间:2016-02-17 20:43:58

标签: xml xpath schematron

我试图匹配缺少xmlns属性的特定元素的任何实例,但是我无法获得与语法匹配的问题。我的xml如下所示:

'F'

我想返回第一个节点,但不是第二个节点。如果我根据第二个节点上显示的 style 属性进行匹配,我可以简单地使用 not(@style),但这对不起作用不(@xmlns:米)。我试图通过搜索具有与URI匹配的值的任何属性来绕过这一点,但同样,这适用于其他属性,但不适用于xmlns:m。是否存在使用XPath匹配/解析xmlns属性所需的某种限制或语法怪癖?

2 个答案:

答案 0 :(得分:6)

  

是否存在使用XPath匹配/解析xmlns属性所需的某种限制或语法怪癖?

是的,有点儿。怪癖是像

这样的东西
xmlns:m="..."

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

检测名称空间声明是不可能的,因为XPath(和XSLT,以及Schematron)不对实际的XML文档进行操作,而是对它们的抽象表示进行操作。在此表示形式(模型)中,缺少名称空间声明,但存在名称空间节点,它间接指向名称空间声明。

一旦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表达式,以解释这一事实。

答案 1 :(得分:2)

如其他地方所述,该问题要求XPath和一般的XML工具不设计用于提取有关命名空间声明的信息。 XPath 旨在能够可靠地检测任何元素或属性所在的名称空间(由其名称空间URI标识,而不是其前缀),以及基于的选择节点命名空间。因此,任何使用标准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的引擎可能不支持它。