我想从 XPath 表达式中提取对文档根目录的所有引用,并在它们之后注入一个自定义根目录。
我正在实现基于某种模式语言创建的 XML 实例文档的一小部分验证(或者更确切地说是修复错误)。该语言提供了指定自包含 XML 块的方法。每个这样的块都在一个单独的文件中定义,并指定 XML 元素层次结构。每个这样的层次结构都有一个或多个属于同一文档根的根元素,就像任何 XML 文档的不可见文档根一样。
然而,这些文件并不知道它们指定的只是更大系统的一部分。这个更大的系统实际上是另一个具有单个顶级 XML 元素的 XML 文档(具有另一个文档根),其中包含由任意数量的此类模式语言文件定义的所有根元素。
XML 层次结构中的任何节点都可能受到 XPath 表达式的约束,该表达式必须计算为真,以便在验证期间元素被视为有效。这就是我的问题的根源。这些 XPath 表达式可能包含绝对位置路径,它引用单个 XML 块的文档根而不是系统的文档根。考虑以下 XML 实例:
<data xmlns="system:uri">
<root-one xmlns="root-one:uri">
<items>
<item>
<group>base</group>
<class>person</person>
<name>John Smith</name>
<description>valid entry</description>
</item>
<item>
<group>base</group>
<class>animal</person>
<name>Dog</name>
<description>invalid entry</description>
</item>
</items>
<item-classes>
<item-class>
<class>person</class>
<group>base</group>
</item-class>
</item-classes>
</root-one>
<root-two xmlns="root-two:uri">
<!-- obscured content -->
</root-two>
</data>
{system:uri}data
代表系统,{root-one:uri}root-one
是{root-two:uri}root-two
两个 XML 块,每个都在它自己的模式语言文件中定义。假设每个root-one/items/item
实例必须满足以下 XPath 条件,在模式语言文件中定义(不要介意current()
,它与 XSLT 中的相同,指的是item
实例之一):
context: /root-one/items/item
assert: group=/root-one/item-classes/item-class[class=current()/class]/group
实际上应该是
context: /data/root-one/items/item
assert: group=/data/root-one/item-classes/item-class[class=current()/class]/group
如何在任何 XPath 表达式中获取对文档根 (/) 的所有引用并使用正确的根注入它们?我无法控制这些表达式是如何形成的,所以它们可以有任何形状和大小,只要它们满足 XPath 1.0 语法,但我必须让它们正确计算。
我目前正在考虑在 java 中编写某种标记器来处理这个问题,但如果有更简单的解决方案,我宁愿不去研究它。表达式是在系统文档上下文中的 Schematron XSLT 转换期间评估的,所以如果我能以某种方式使用 XSLT 实现路径修复,那将是完美的。但是,我已准备好接受任何可能导致我找到解决方案的指示。
编辑01
这就是包含 XPath 表达式的示例文件的样子(在我的脑海中)。我希望转换@test
属性的内容。属性的值@context
是微不足道的,因为它总是具有相似的结构。
<?xml version="1.0" encoding="utf-8"?>
<iso:schema xmlns="http://purl.oclc.org/dsdl/schematron"
xmlns:iso="http://purl.oclc.org/dsdl/schematron"
xmlns:sch="http://www.ascc.net/xml/schematron"
xmlns:tl="toplevel:uri"
xmlns:r1="root-one:uri"
xmlns:r2="root-two:uri">
<iso:ns prefix="tl" uri="toplevel:uri" />
<iso:ns prefix="r1" uri="root-one:uri" />
<iso:ns prefix="r2" uri="root-two:uri" />
<iso:pattern>
<iso:rule context="/r1:root-one/r1:items/r1:item">
<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
</iso:rule>
</iso:pattern>
</iso:schema>
请注意,@test
属性的值可以是任何有效的 XPath 1.0 表达式。我想找到一个通用解决方案,它可以找到表达式中任何位置定义的任何文档根('/'),并使用自定义根元素注入它。实际文件可能包含任意数量的iso:pattern
元素、iso:rule
元素等。
编辑02
对于上面的示例,想要的结果是以下iso:assert
元素:
<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
编辑03
作为回应您如何决定 /r1:root-one/ 必须以 '/tl:data' 开头?能否请您描述一下规则?——迪米特雷·诺瓦切夫
/tl:data
表示通过将多个其他 XML 文档组合成一个文档而创建的文档的根元素。这些文档的内容作为子元素附加到此根元素。r1:root-one
成为这样的孩子之一。XPath 约束是描述元素结构r1:root-one
外观的模式定义的一部分,旨在仅在此子 XML 文档的上下文中工作。当子 XML 文档被附加到“父”文档时,如果表达式中存在绝对路径,它们就失去了意义。因此,如果表达式包含/r1:root-one
this 在新文档中将没有任何意义(其中没有root-one
根元素,tl:data
是唯一的根)。我想找到所有这样的案例(/r1:root-one/
)并将它们转换(变成/tl:data/r1:root-one/
) 所以表达式在新文档的上下文中起作用。
很难指定确切的规则。每个/
出现在路径开头的“”(因此引用子 XML 文档的文档根)都应该替换为“ /tl:data/
”,因此它现在引用新创建的文档的文档根。
编辑04
如上文所述,该解决方案应该适用于任何可以想象的 XPath 表达式。其他示例(来自 r1 命名空间的虚构元素是由我组成的 - 这在我脑海中听起来更好):
<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />
应该成为
<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=/tl:data/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />
编辑05
我现在可以选择切换到 XSLT 2.0 处理器。所以我会接受 XSLT 2.0 解决方案。
事实上,如果有人可以为我提供一个XSLT 正则表达式,该表达式将匹配/
表示 XPath 1.0 表达式中的文档根的符号,这将解决我的问题(我将使用该replace()
函数)。我一直在研究XPath 1.0 语法,但还没有任何有用的东西。