Grouping in XSLT

Tag: xslt , xslt-grouping Author: chris805335680 Date: 2012-02-29

Have the source XML:

<root>
      <element Key="card1_id">123</element>
      <element Key="card1_balance">500</element>
      <element Key="card2_id">456</element>
      <element Key="card2_balance">800</element>
      .............................................
      <element Key="card(n)_id">999</element>
      <element Key="card(n)_balance">8000</element>
</root>

Where n - the number of cards

With XSLT i need to get a HTML:

<b>Card 1:</b>   
  ID: 123 <br/> 
  Balance: 500 <br/>  

<b>Card 2:</b>   
  ID: 456<br/>  
  Balance: 800<br/> 
 ..................
<b>Card n:</b>   
  ID: 999<br/>  
  Balance: 8000<br/>

How to organize such grouping?

Best Answer

Use Muechian grouping with XSLT 1.0:

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

  <xsl:key name="k1" match="element" use="translate(@Key, translate(@Key, '0123456789', ''), '')"/>

  <xsl:output method="html" indent="yes"/>

  <xsl:template match="root">
    <div>
      <xsl:apply-templates select="element[generate-id() = generate-id(key('k1', translate(@Key, translate(@Key, '0123456789', ''), ''))[1])]" mode="group"/>
    </div>
  </xsl:template>

  <xsl:template match="element" mode="group">
    <b>Card <xsl:value-of select="translate(@Key, translate(@Key, '0123456789', ''), '')"/>:</b>
    <xsl:apply-templates select="key('k1', translate(@Key, translate(@Key, '0123456789', ''), ''))"/>
  </xsl:template>

  <xsl:template match="element">
    <xsl:value-of select="concat(substring-after(., '_'), ': ', .)"/>
    <br/>
  </xsl:template>

</xsl:stylesheet>

With that stylesheet Saxon 6.5.5 transforms

<root>
      <element Key="card1_id">123</element>
      <element Key="card1_balance">500</element>
      <element Key="card2_id">456</element>
      <element Key="card2_balance">800</element>
      .............................................
      <element Key="card3_id">999</element>
      <element Key="card3_balance">8000</element>
</root>

into

<div><b>Card 1:</b>: 123<br>: 500<br><b>Card 2:</b>: 456<br>: 800<br><b>Card 3:</b>: 999<br>: 8000<br></div>

comments:

Thanks! But you may be mistaken in the Saxon transforms?

Other Answer1

You don't say if you are using XSLT 1.0 or 2.0.

Also you don't really need grouping per se, to achieve the output.

e.g. the below would emit similar to what you want, with no grouping. Now if your data comes in a different order, then yes, you'd want grouping.

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

<xsl:output method="html"/>

<xsl:template match="/root/element[ends-with(@Key,'id')]">
 <b><xsl:value-of select="replace(@Key,'(.*)(\d.*)_id','$1 $2:')"/></b>   
  ID: <xsl:value-of select="."/> <br/> 
  Balance: <xsl:value-of select="following-sibling::element"/> <br/>
</xsl:template>

<xsl:template match="element"/>

</xsl:stylesheet>

comments:

I need using XSLT 1.0. Your code not working. I test him in w3schools.com/xsl/…
Dino Fancellu: The code in your answer is written in XSLT 2.0 -- you need to edit and change the version attribute of xsl:stylesheet to: "2.0". Otherwise this is confusing and people will try to run the transformation with XSLT 1.0 processors only to find out that errors or unexpected results are produced.