XSLT: match the first child element

I am trying to match the first bar element that happens as a descendant of the foo element in the xsl matching pattern and fights. Initial Attempt:

 <xsl:template match="//foo//bar[1]"> ... </xsl:template> 

fails because there are several bar elements that match. So:

 <xsl:template match="(//foo//bar)[1]"> ... </xsl:template> 

but it fails to compile.

+6
source share
3 answers

Tricky I don’t know how effective it would be or otherwise, but you could rotate the template by the head and move the logic to the predicate (which is allowed to use axes other than the child, attribute and // ):

 <xsl:template match="foo//bar[not(preceding::bar/ancestor::foo)]"> 

(any bar inside a foo if there is no other bar-inside-foo in front of it). Alternatively, you can try a key trick similar to the way Muenchian grouping works, which might be more efficient

 <!-- trick key - all matching nodes will end up with the same key value - all we care about is finding whether a particular node is the first such node in the document or not. --> <xsl:key name="fooBar" match="foo//bar" use="1" /> <xsl:template match="foo//bar[generate-id() = generate-id(key('fooBar', 1)[1])]"> 
+2
source

You cannot do this with matching expressions. In fact, you can do this with matching expressions, just not in every XSLT processor, as it seems. See Comments.

I would use <xsl:if> .

 <xsl:template match="foo//bar"> <xsl:if test="generate-id() = generate-id(ancestor::foo[1]//bar)"> <!-- ... --> </xsl:if> </xsl:template> 

This ensures that only the first child <bar> processed for <foo> (!).

NB: if node-set is specified, generate-id() returns the identifier of the first node in the set.

+2
source

An alternative solution is based on an “empirical rule” using extended XPATH with “select” rather than a “match”. Match XML with templates simply by name, like foo, not even // foo.

 <?xml version="1.0" encoding="UTF-8"?> <foo> <bar>bar1<bar>bar2</bar></bar> <bar>bar3</bar> </foo> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="foo"> <xsl:apply-templates select="descendant::bar[1]"/> </xsl:template> <xsl:template match="bar"> <!--only the first bar was selected --><xsl:value-of select="text()"/> </xsl:template> </xsl:stylesheet> 
0
source

Source: https://habr.com/ru/post/970959/


All Articles