Question

I'm trying to perform an XSL transformation on the XML below:

<root>
    <row>
        <field1>2014-04-01</field1>
        <field2>AAA123</field2>
        <field3>text1</field3>
        <field4>text1</field4>
        <field5>text1</field5>
    </row>
    <row>
        <field1>2014-04-01</field1>
        <field2>AAA123</field2>
        <field3>text2</field3>
        <field4>text2</field4>
        <field5>text2</field5>
    </row>
    <row>
        <field1>2014-04-01</field1>
        <field2>BBB456</field2>
        <field3>text3</field3>
        <field4>text3</field4>
        <field5>text3</field5>
    </row>
</root>

What I'm trying to do is group <row> elements and their attributes based on a common <field1> and <field2> combination. The output I'm trying to achieve is something like the below:

<output>
    <common>
        <field1>2014-04-01</field1>
        <field2>AAA123</field2>
        <attributes>
            <field3>text1</field3>
            <field4>text1</field4>
            <field5>text1</field5>
        </attributes>
        <attributes>
            <field3>text2</field3>
            <field4>text2</field4>
            <field5>text2</field5>
        </attributes>
    </common>
    <common>
        <field1>2014-04-01</field1>
        <field2>BBB456</field2>
        <attributes>
            <field3>text3</field3>
            <field4>text3</field4>
            <field5>text3</field5>
        </attributes>
    </common>
</output>

I would assume this is done by using some kind of <xsl:for-each> loop, but I'm struggling to find any documentation (or answer on this site) that explains how to build an <xsl:for-each> loop that loops through a combination of nodes/values.

I'm relatively new to XSL and am a little stuck with where to approach this one. If anyone could point me to some documentation or shed any light on how to approach this, that would be greatly appreciated. For info, I'm working with xslt-2.0. Thanks in advance.

Was it helpful?

Solution

Use

<xsl:template match="root">
  <output>
    <xsl:for-each-group select="row" group-by="concat(field1, '|', field2)">
      <common>
       <xsl:copy-of select="field1, field2"/>
       <xsl:apply-templates select="current-group()"/>
     </common>
    </xsl:for-each-group>
  </output>
</xsl:template>

<xsl:template match="row">
  <attributes>
    <xsl:copy-of select="field3, field4, field5"/>
  </attributes>
</xsl:template>

OTHER TIPS

You can use xsl:for-each-group and group by field1 and field2.

Example...

XML Input

<root>
    <row>
        <field1>2014-04-01</field1>
        <field2>AAA123</field2>
        <field3>text1</field3>
        <field4>text1</field4>
        <field5>text1</field5>
    </row>
    <row>
        <field1>2014-04-01</field1>
        <field2>AAA123</field2>
        <field3>text2</field3>
        <field4>text2</field4>
        <field5>text2</field5>
    </row>
    <row>
        <field1>2014-04-01</field1>
        <field2>BBB456</field2>
        <field3>text3</field3>
        <field4>text3</field4>
        <field5>text3</field5>
    </row>
</root>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/*">
        <output>
            <xsl:for-each-group select="row" group-by="concat(field1,'|',field2)">
                <common>
                    <xsl:apply-templates select="field1,field2"/>
                    <xsl:apply-templates select="current-group()"/>
                </common>
            </xsl:for-each-group>
        </output>
    </xsl:template>

    <xsl:template match="row">
        <attributes>
            <xsl:apply-templates select="*[not(self::field1) and not(self::field2)]"/>
        </attributes>
    </xsl:template>

</xsl:stylesheet>

XML Output

<output>
   <common>
      <field1>2014-04-01</field1>
      <field2>AAA123</field2>
      <attributes>
         <field3>text1</field3>
         <field4>text1</field4>
         <field5>text1</field5>
      </attributes>
      <attributes>
         <field3>text2</field3>
         <field4>text2</field4>
         <field5>text2</field5>
      </attributes>
   </common>
   <common>
      <field1>2014-04-01</field1>
      <field2>BBB456</field2>
      <attributes>
         <field3>text3</field3>
         <field4>text3</field4>
         <field5>text3</field5>
      </attributes>
   </common>
</output>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top