Reputation: 17
I have to select and apply templates for all specific node between two nodes but stop at specific node..... for example in this code:
.........
<p class="capo">1</p>
<p class="busta">prima</p>
<p class="dialogo1">dati</p>
<p class="dialogo2">dati</p>
<p class="dialogo3">dati</p>
<p class="busta">seconda:</p>
<p class="dialogo4">dati</p>
<p class="dialogo5">dati</p>
<p class="capo">2</p>
<p class="capo">3</p>
<p class="dialogo6">dati</p>
<p class="capo">4</p>
<p class="busta">terza:</p>
..........
I need to select all <p class="dialogo"/>
between and after tag <p class="busta"/>
, but if I found a <p class="capo"/>
I need to stop...
The result must be like this:
<busta>
<p class="dialogo1">dati</p>
<p class="dialogo2">dati</p>
<p class="dialogo3">dati</p>
</busta>
<busta>
<p class="dialogo4">dati</p>
<p class="dialogo5">dati</p>
</busta>
..........
In "template match="p[@class='busta']"
I am using following code but selection dont stop when find capo and catch also dialogo 6 "select="following-sibling::p[@class='busta'] [generate-id(preceding-sibling::p[@class='busta'][1]) = generate-id(current())]"/>"
Thanks in advance
Upvotes: 1
Views: 809
Reputation: 167506
Assuming XSLT 2 or 3 this is a text book use of for-each-group group-starting-with="p[@class = 'busta']"
respectively group-ending-with="p[@class = 'capo']"
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes" />
<xsl:template match="body">
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="p[@class = 'busta']">
<xsl:if test="self::p[@class = 'busta']">
<busta>
<xsl:for-each-group select="current-group()[position() gt 1]" group-ending-with="p[@class = 'capo']">
<xsl:if test="position() eq 1">
<xsl:apply-templates select="current-group()[not(self::p[@class = 'capo'])]"/>
</xsl:if>
</xsl:for-each-group>
</busta>
</xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That transforms
<body>
<p class="capo">1</p>
<p class="busta">prima</p>
<p class="dialogo1">dati</p>
<p class="dialogo2">dati</p>
<p class="dialogo3">dati</p>
<p class="busta">seconda:</p>
<p class="dialogo4">dati</p>
<p class="dialogo5">dati</p>
<p class="capo">2</p>
<p class="capo">3</p>
<p class="dialogo6">dati</p>
<p class="capo">4</p>
<p class="busta">terza:</p>
</body>
into
<body>
<busta>
<p class="dialogo1">dati</p>
<p class="dialogo2">dati</p>
<p class="dialogo3">dati</p>
</busta>
<busta>
<p class="dialogo4">dati</p>
<p class="dialogo5">dati</p>
</busta>
<busta/>
</body>
See https://xsltfiddle.liberty-development.net/6qM2e2h.
For XSLT 2 you need to spell out the xsl:mode
as the identity transformation template
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
To give you an example where the p
elements are not children of an input element but passed in as a parameter as a sequence of element nodes, here is how that would look (https://xsltfiddle.liberty-development.net/6qM2e2h/1):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes" />
<xsl:param name="paragraphs" as="element()*">
<p class="capo">1</p>
<p class="busta">prima</p>
<p class="dialogo1">dati</p>
<p class="dialogo2">dati</p>
<p class="dialogo3">dati</p>
<p class="busta">seconda:</p>
<p class="dialogo4">dati</p>
<p class="dialogo5">dati</p>
<p class="capo">2</p>
<p class="capo">3</p>
<p class="dialogo6">dati</p>
<p class="capo">4</p>
<p class="busta">terza:</p>
</xsl:param>
<xsl:template match="/">
<xsl:for-each-group select="$paragraphs" group-starting-with="p[@class = 'busta']">
<xsl:if test="self::p[@class = 'busta']">
<busta>
<xsl:for-each-group select="current-group()[position() gt 1]" group-ending-with="p[@class = 'capo']">
<xsl:if test="position() eq 1">
<xsl:apply-templates select="current-group()[not(self::p[@class = 'capo'])]"/>
</xsl:if>
</xsl:for-each-group>
</busta>
</xsl:if>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
So where and how and with which input selection you use that for-each-group
is up to you, that does not depend on template matching. You could write a template or function taking the sequence of p
elements as an input parameter and put the for-each-group
into the template or function body.
Upvotes: 1