Mysakyu
Mysakyu

Reputation: 13

XPath joining multiple elements

I'm looking a way to join every two elements together using XPath2.0:

<item>
    <element class='1'>el1</element>
    <element class='2'>el2</element>
    <break>break</break>
    <element class='1'>el3</element>
    <element class='2'>el4</element>
    <break>break</break>
    <element class='1'>el5</element>
    <element class='2'>el6</element>
    <break>break</break>
    <element class='1'>el7</element>
    <element class='2'>el8</element>
</item>

I am hoping the result will be like:

el1el2
el3el4
el5el6
el7el8

The are "breaks" between two meaningful elements, and there are also classes to help out, but still I cannot get it done.

Since I'm not familiar with XPath, this is what I can come up so far, and turned out to be wrong, since concatenate needs at least two arguments...

//item/concat(element[preceding-sibling::break | following-sibling::break])

Upvotes: 1

Views: 4943

Answers (3)

Max Toro
Max Toro

Reputation: 28608

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

   <xsl:output omit-xml-declaration="yes"/>

   <xsl:template match="item">
      <xsl:for-each-group select="*" group-starting-with="break">
         <xsl:if test="current-group()[1][self::break]">
            <xsl:text>&#13;&#10;</xsl:text>
         </xsl:if>
         <xsl:value-of select="current-group()[self::element]" separator=""/>
      </xsl:for-each-group>
   </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Ian Roberts
Ian Roberts

Reputation: 122364

//item/element[@class='1']/concat(., following-sibling::element[1])

You want your result sequence to contain one item for each of the class='1' elements, the value of that item being the concatenation of that element and its next sibling (the matching class='2').

Upvotes: 2

rene
rene

Reputation: 42414

I'm not sure if you're also open for an XSLT 1.0 solution but this works for me with your input xml:

  <xsl:template match="/item">
     <xsl:apply-templates select="element[1]|break"/> 
  </xsl:template>

  <xsl:template match="element[1]">
    <xsl:text>
</xsl:text>
    <xsl:value-of select="."/>
    <xsl:value-of select="following-sibling::*[1]"/>
  </xsl:template>

  <xsl:template match="break">
    <xsl:text>
</xsl:text>
    <xsl:value-of select="following-sibling::*[1]"/>
    <xsl:value-of select="following-sibling::*[2]"/>
  </xsl:template>

I have two templates that match either the start element or the break element. I use the following-sibling axis to get to the next two elements. The <xsl:text> elements are there to force a linebreak.

Upvotes: 1

Related Questions