Merge adjacent sibling nodes with XSLT
I have a question that caused me a terrible headache. Please help me. The input is:
<body>
<p class="section"> section 1 </p>
<p class="code"> some code </p>
<p class="code"> following code </p>
<p class="code"> following code </p>
<p class="section"> section 2 </p>
<p class="code"> other code </p>
<p class="code"> following code </p>
<p class="code"> following code </p>
<p class="section"> section 3 </p>
<p class="code"> still other code </p>
<p class="code"> following </p>
<p class="code"> following </p>
</body>
The output I'd like:
<body>
<p class="section"> section 1 </p>
<pre> some code following code开发者_如何学编程 following code </pre>
<p class="section"> section 2 </p>
<pre> other code following code following code </pre>
<p class="section"> section 3 </p>
<pre> still other code following following </pre>
</body>
The problem is to collapse to a <pre>
tag all adjacent <p class="code">
tags. Don't find a way to do this using XSLT. Do you think a solution exists?
You don't need to rebuild your XML, take a look here: XSLT Grouping Siblings.
With XSLT 2.0 you can use for-each-group group-adjacent as follows:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(self::p[@class = 'code'])">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<pre>
<xsl:apply-templates select="current-group()/node()"/>
</pre>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You can use XSLT 2.0 with Saxon 9 or with AltovaXML tools.
Something like this should work:
<xsl:template match="body">
<xsl:apply-templates select="p[@class='section']" />
</xsl:template>
<xsl:template match="p[@class='section']">
<xsl:copy-of select="."/>
<pre>
<xsl:variable name="code" select="following-sibling::p[@class='code']" />
<xsl:for-each select="following-sibling::p">
<xsl:variable name="index" select="position()"/>
<xsl:if test="generate-id(.)=generate-id($code[$index])">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</pre>
</xsl:template>
The issue is that your XML isn't structured enough for a simple XSLT solution.
The different "section"s are not really setup in a way that is easy to extract them. If you have control over the input XML see if you can change it to something like this:
<body>
<p class="section"> section 1
<p class="code"> some code </p>
<p class="code"> following code </p>
<p class="code"> following code </p>
</p>
<p class="section"> section 2
<p class="code"> other code </p>
<p class="code"> following code </p>
<p class="code"> following code </p>
</p>
<p class="section"> section 3
<p class="code"> still other code </p>
<p class="code"> following </p>
<p class="code"> following </p>
</p>
</body>
This will let you define a xsl-template
for "section"s in which you can xsl-foreach
over the "code" classes.
精彩评论