xpath:测试节点是否是指定ok元素以外的唯一子节点

时间:2015-01-15 21:23:57

标签: xml xslt xpath

我想编写一个xsl模板,检查给定节点是否是唯一的子节点,而不是某些指定的元素:

在此示例中,< target />将更改为< hit />因为它是唯一的< target />节点,只有< ok />节点在它之前

<root>
<!-- this is ok, the ok nodes are at the top, followed by only 1 target -->
<mynode>
    <ok1/>
    <ok2/>
    <target/>
</mynode>

<!-- will fail, bad element before target -->
<mynode>
    <ok1/>
    <ok2/>
    <bad/>
    <target/>
</mynode>

<!-- no match, multiple target nodes -->
<mynode>
    <ok1/>
    <ok2/>
    <target/>
    <target/>
</mynode>
</root>

我正在使用这个xpath:

<xsl:template match="target[not(following-sibling::*)]
                       [not(preceding-sibling::target)]
                       [not(preceding-sibling::*[starts-with(name(), 'bad' or 'hello')])]
                 ">
    <hit>
        <xsl:apply-templates/>
    </hit>
</xsl:template>

在最后一个谓词中,我是否必须明确指出我不想要的任何节点?我可以这样吗

not(preceding-sibling::*[not(starts-with(name(), 'ok'))])

感谢

2 个答案:

答案 0 :(得分:3)

这个怎么样:

<xsl:template match="target[count(../*) = 
                            count(../*[starts-with(name(), 'ok')]) + 1]">
    <hit>
        <xsl:apply-templates/>
    </hit>
</xsl:template>

解释是匹配target if:

  • 其父的所有子元素的数量等于
  • 其父级的所有好子元素的数量加上一个(本身)

修改如果您只想匹配其父级的 last 子元素(您在问题中未这样说,但您的示例提示),您可以将and not(following-sibling::*)添加到上面的谓词中,或者这是另一种方法:

<xsl:template match="target[not(following-sibling::*) and 
                            not(preceding-sibling::*[not(starts-with(name(), 'ok'))])
                           ]">

但你似乎已经自己想出了一个。

最后,如果您真正想要做的是允许某些特定的OK元素并且根据前缀不匹配名称,则可以使用self::

<xsl:template match="target[count(../*) = 
                            count(../*[self::allgood or self::great]) + 1]">

<xsl:template match="target[not(following-sibling::*) and 
                            not(preceding-sibling::*[not(self::allgood or
                                                         self::great     )]
                               )]">

答案 1 :(得分:0)

[not(preceding-sibling::*[starts-with(name(), 'bad' or 'hello')])]

不能用作'bad'或'hello'是布尔值或字符串 你也不需要使用double not() 并简单地做

preceding-sibling::*[starts-with(name(),'ok')]

您还可以创建白名单或黑名单,并使用contains()XPath函数对其进行迭代,例如:

<xsl:variable name="oks" select="ok1 ok2 ok3"/>

然后匹配

preceding-sibling::*[contains($oks, name())]