我遇到了一个有趣的需求,我已经有自己的工作实现,但我正在努力确保我已经涵盖了所有极端情况。
我想要实现的最好描述为“XPath 条件委托”。
假设某种晦涩的模式语言可以使用任意 XPath 条件来约束实例元素。这些条件被转换为 Schematron 断言,然后应用于 XML 实例文档。为了使其工作,每个条件都需要一个上下文节点来测试表达式(这就像说:“在 XML 实例文档中找到某个元素,然后检查 XPath 条件是否适用于它”)。事情是; 可以为虚拟/抽象节点定义我的模式语言中的某些条件,这些节点从未出现在实例文档中(xsd:choice 在这里可能是一个合适的类比——它只存在于语言文件中,而不存在于实例中)。所以我想,好吧,让我们简单地将条件从父虚拟节点委托给子节点,实际出现在 XML 实例中。结果很好!如果您只使用绝对 XPath 位置路径,那就是......
模式语言的假设示例:
element smth {
element c-enabled;
choice c {
assert "c-enabled = 'true'"; <!-- context node is <smth>, first instatiable ancestor -->
element one;
element two;
}
element foo;
}
这基本上是说,允许<smth>
具有 a<c-enabled>
和一个<foo>
子元素的元素,并且 if<c-enabled>
的值'true'
也允许 a<one>
或一个<two>
子元素。有效实例文档的示例:
<smth>
<c-enabled>true</c-enabled>
<two/>
<foo/>
</smth>
因此,如前所述,我决定将条件委托给<one>
and <two>
,实质上是在运行时将上述模式重构为以下内容:
element smth {
element c-enabled;
choice c {
element one {
assert "c-enabled = 'true'"; <!-- context node is <one>, never satisfied since <one> has no <c-enabled> child -->
}
element two {
assert "c-enabled = 'true'"; <!-- context node is <two>, never satisfied since <two> has no <c-enabled> child -->
}
}
element foo;
}
这显然只有在使用绝对路径编写 XPath 条件时才有效。每次在实例文档中遇到<one>
or<two>
时,都会通过将它们用作初始上下文节点来检查条件(它们是current()
XSLT 中返回的内容)。
因此,在阅读了一些XPath 规范并使用结合此语法的精彩ANTLR4 工具创建 XPath 解析器并实现了转换解析树步行器之后,我现在将原始模式重构为:
element smth {
element c-enabled;
choice c {
element one {
assert "../c-enabled = 'true'"; <!-- u-huh, this is what we're after -->
}
element two {
assert "../c-enabled = 'true'"; <!-- u-huh, this is what we're after -->
}
}
element foo;
}
请注意,这只是我正在处理的 XPath 条件的一个简单示例(这不是简单地编写正则表达式来处理它的问题),因为它们是完全任意的,由创建模式定义的人组成。关于它们,我唯一可以确定的是它们是有效的 XPath 1.0 表达式(只有基本函数,没有变量)。
所以我的问题是:根据 XPath 1.0 规范,XPath 表达式中可能出现引用初始上下文节点(以相对方式)的构造的位置是什么?
我目前的假设是:任何在解析树中没有以下祖先的RelativeLocationPath : AbsoluteLocationPath和Predicate。如果 RelativeLocationPath 前面有一个 AbsoluteLocationPath,则该组合形成一个绝对构造。在谓词中,所有路径都指向不同的上下文节点或者是绝对的。
我假设这是基于我制作的这张图(表示使用我上面提到的语法解析 XPath 时如何调用产品):
下面是一些我需要在 XPath 表达式中找到的地方的示例。基本上是在寻找这些(可能还有其他)的正式定义。
(../area-type = 'stub') or (../area-type = 'nssa')
^ ^
(../../../cacheMode != 'ipfix:immediate')
^
address-family='ipV4' and safi='nlri-unicast'
^ ^
(../../../cacheMode != 'ipfix:immediate') and ((count(../ieEnterpriseNumber) = 0) or (../ieEnterpriseNumber != 29305))
^ ^ ^