tags:

views:

241

answers:

4

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?

A: 

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.

Oded
Subjective, but: Mixing simple-content and elements doesn't look like a good idea.
Filburt
No way, it's not really xml input; it's html that I'm trying to clean. So...the challenge is to work on that.
emime
@Filburt - I agree, but trying to minimize change...
Oded
+4  A: 

You don't need to rebuild your XML, take a look here: XSLT Grouping Siblings.

Rubens Farias
Nice solution there!
Oded
@Rubens Farias: Thanks! :-)
Tomalak
+1  A: 

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>
Lucero
Great! I integrated this solution into my xslt and it works perfectly!
emime
Thanks for the feedback. :)
Lucero
+1  A: 

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.

Martin Honnen
I'm using XSLT 2.0 and Saxon 9. So I made an attempt.It was little more difficult to integrate in my real xsl, but worked at the end.for-each-group is a useful xslt 2.0 element to know better.Thanks
emime