Reputation: 1614
I want to write an xsl template that checks to see if a given node is the only child node, other than certain specified elements :
in this example, <target/> will be changed to <hit/> as it is the only <target/> node, and only <ok/> nodes precede it
<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>
I was using this 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>
In that last predicate, do I have to indicate specifically any node I don't want? Can I something like
not(preceding-sibling::*[not(starts-with(name(), 'ok'))])
thanks
Upvotes: 1
Views: 1345
Reputation: 101682
How about this:
<xsl:template match="target[count(../*) =
count(../*[starts-with(name(), 'ok')]) + 1]">
<hit>
<xsl:apply-templates/>
</hit>
</xsl:template>
The interpretation is to match target
if:
Edit If you only want to match the element if it's the last child of its parent (you didn't say so in your question, but your examples suggest that), you can add and not(following-sibling::*)
to the predicate above, or here is an alternative approach:
<xsl:template match="target[not(following-sibling::*) and
not(preceding-sibling::*[not(starts-with(name(), 'ok'))])
]">
but you seem to have already figured that one out on your own.
Lastly, if what you actually want to do is allow certain specific OK elements and not match the names based on a prefix, you can use self::
for this:
<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 )]
)]">
Upvotes: 3
Reputation: 435
[not(preceding-sibling::*[starts-with(name(), 'bad' or 'hello')])]
won't work as 'bad' or 'hello' is boolean or string You also don't need to use double not() and simply do
preceding-sibling::*[starts-with(name(),'ok')]
You can also create a whitelist or a blacklist and iterate over it with a contains() XPath function, for example:
<xsl:variable name="oks" select="ok1 ok2 ok3"/>
and then match
preceding-sibling::*[contains($oks, name())]
Upvotes: 0