Carthyc
Carthyc

Reputation: 23

Convert raw input XML to structured XML using XSL

I tried using XSL with "for-each" I need to group the value based on CATEGORY and TYPE.

Any help?

Input RAW xml:

<?xml version = '1.0' encoding = 'UTF-8'?>
<ROWSET>
<ROW>
<ID>12345</ID>
<TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>1</VALUE>
<LABEL>ISO1</LABEL>
</ROW>
<ROW>
<ID>12345</ID>
<TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>2</VALUE>
<LABEL>ISO2</LABEL>
</ROW>
<ROW>
<ID>12345</ID>
<TYPE>TYP1</TYPE>
<SUBTYPE>SUBTYP2</SUBTYP>
<CATEGORY>A1</CATEGORY>
<VALUE>3</VALUE>
<LABEL>ISO3</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>1</VALUE>
<LABEL>ISO1</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>1</VALUE>
<LABEL>ISO1</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>2</VALUE>
<LABEL>ISO2</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP2</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>3</VALUE>
<LABEL>ISO3</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP2</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>2</VALUE>
<LABEL>ISO2</LABEL>
</ROW>
<ROW>
<ID>67890</ID>
<TYPE>TYP2</TYPE>
<CATEGORY>A1</CATEGORY>
<VALUE>3</VALUE>
<LABEL>ISO1</LABEL>
</ROW>
</ROWSET>

Expected Result: I need to group the values based on Category and TYPE (see below)

<?xml version = '1.0' encoding = 'UTF-8'?>
<ROWSET>
<ROW>
<ID>12345</ID>
    <TYPE>TYP1</TYPE>
<CATEGORY>A1</CATEGORY>
    <GRP1>
    <VALUE>1</VALUE>
    <LABEL>ISO1</LABEL>
    <VALUE>2</VALUE>
    <LABEL>ISO2</LABEL>
    <VALUE>3</VALUE>
    <LABEL>ISO3</LABEL>
    </GRP1>         
</ROW>
<ROW>
<ID>67890</ID>
  <TYP>
    <TYPE>TYP1</TYPE>
    <TYPE>TYP2</TYPE>
  </TYP>
<CATEGORY>A1</CATEGORY>
    <GRP1>
    <VALUE>1</VALUE>
    <LABEL>ISO1</LABEL>
    <VALUE>2</VALUE>
    <LABEL>ISO2</LABEL>
    <VALUE>3</VALUE>
    <LABEL>ISO3</LABEL>
    </GRP1>         

XSL used: It needs some corrections. (Thanks to @Parfait for the code)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="categkey" match="ROW" use="CATEGORY" />

<xsl:template match="ROWSET">
<xsl:copy>
  <xsl:for-each select="ROW[generate-id()= generate-id(key('categkey', CATEGORY)[1])]">
    <xsl:copy>
      <xsl:copy-of select="ID"/>
      <xsl:copy-of select="TYPE"/>
      <xsl:copy-of select="CATEGORY"/>
      <GRP1>
        <xsl:for-each select="key('categkey', CATEGORY)">            
            <xsl:copy-of select="VALUE"/>
            <xsl:copy-of select="LABEL"/>            
        </xsl:for-each>
      </GRP1>
    </xsl:copy>
  </xsl:for-each>
    </xsl:copy>
 </xsl:template>
</xsl:transform>

Thanks! Carthyc

Upvotes: 2

Views: 213

Answers (1)

Parfait
Parfait

Reputation: 107687

For grouping in XSLT 1.0, consider the Muenchian Method which indexes the document according to a key and can run various operations with that key:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="categkey" match="ROW" use="CATEGORY" />

  <xsl:template match="ROWSET">
    <xsl:copy>
      <xsl:for-each select="ROW[generate-id()= generate-id(key('categkey', CATEGORY)[1])]">
        <xsl:copy>
          <xsl:copy-of select="ID"/>
          <xsl:copy-of select="TYPE"/>
          <xsl:copy-of select="CATEGORY"/>
          <GRP1>
            <xsl:for-each select="key('categkey', CATEGORY)">            
                <xsl:copy-of select="VALUE"/>
                <xsl:copy-of select="LABEL"/>            
            </xsl:for-each>
          </GRP1>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

UPDATE

Borrowing from @Tim C's previous answer (the XSLT guru who first edited your post), for multiple keys such as on ID and its siblings, consider the below:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="idkey" match="ROW" use="ID" />
<xsl:key name="typekey" match="ROW" use="concat(ID, TYPE)"/>
<xsl:key name="categkey" match="ROW" use="concat(ID, CATEGORY)" />
<xsl:key name="valuekey" match="ROW" use="concat(ID, VALUE)" />
<xsl:key name="labelkey" match="ROW" use="concat(ID, LABEL)" />

  <xsl:template match="ROWSET">
    <xsl:copy>
      <xsl:for-each select="ROW[generate-id()= generate-id(key('idkey', ID)[1])]">
        <xsl:copy>
          <xsl:copy-of select="ID"/>
          <TYP>            
            <xsl:for-each select="key('idkey', ID)[generate-id()= generate-id(key('typekey', concat(ID, TYPE))[1])]">            
                <xsl:copy-of select="TYPE"/>                                    
            </xsl:for-each>
          </TYP>
          <xsl:copy-of select="CATEGORY"/>

          <GRP1>
            <xsl:for-each select="key('idkey', ID)[generate-id()= generate-id(key('valuekey', concat(ID, VALUE))[1])]">
              <xsl:copy-of select="VALUE"/>          
              <xsl:copy-of select="LABEL"/>
            </xsl:for-each>
          </GRP1>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Upvotes: 1

Related Questions