Anita N
Anita N

Reputation: 1

xslt transormation from xml to xml using group by on node and attribute

I need help on transforming xml to another xml format using xslt. Tried using .xsl file below but its not working, appreciate help.

 <?xml version="1.0" encoding="UTF-8"?>
  <xsl:stylesheet version="1.0"   
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method ="xml" indent ="yes"/>
  <xsl:template match="Mappings/Source/MapToType/MapTo/*">
  <xsl:copy>
      <xsl:for-each-group select="Source" group-by="name">
          <xsl:apply-templates select="." />
      </xsl:for-each-group>
  </xsl:copy>
  </xsl:template>

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

Below is source XML

  <?xml version='1.0' encoding='UTF-8'?>
   <Mappings version="1.0">
   <Source name="testA" type="testType" reg="Good">
    <MapToType type="Tiger">
        <MapTo name="Org" reg="Ver1.0"/>
    </MapToType>
   </Source>
  <Source name="testB" type="testType" reg="Good">
    <MapToType type="Tiger">
        <MapTo name="Org2" reg="Ver2.0"/>
    </MapToType>
  </Source>
  <Source name="testA" type="testType" reg="Good">
    <MapToType type="Tiger">
        <MapTo name="Org3" reg="Ver3.0"/>
    </MapToType>
   </Source>
</Mappings>

Output expecting like below.

 <Mappings version="1.0">
 <Source name="testA" type="testType" reg="Good">
    <MapToType type="Tiger">
        <MapTo name="Org" reg="Ver1.0"/>
        <MapTo name="Org3" reg="Ver3.0"/>
    </MapToType>
  </Source>
  <Source name="testB" type="testType" reg="Good">
    <MapToType type="Tiger">
       <MapTo name="Org2" reg="Ver2.0"/>
    </MapToType>
  </Source>
  </Mappings>

Anybody has solution to how to write a condition to display as output.xml?

Upvotes: 0

Views: 40

Answers (1)

Tim C
Tim C

Reputation: 70598

First note, xsl:for-each-group is an XSLT 2.0 command, so you should really make sure you are using a processor that can process XSLT 2.0

Anyway, one reason it is not working is because when you do <xsl:for-each-group select="Source" group-by="name">, you are in a template that matches MapTo. As the select will be relative to this MapTo element, you will be looking for child elements named Source, of which there are none.

(EDIT: Or, as Martin Honnen rightly points out in comments, the template actually matches child elements of MapTo, not MapTo itself, and so won't match anything, and so will never be used....)

You should really change your template to match Mappings instead...

<xsl:template match="Mappings">
  <xsl:copy>
    <xsl:apply-templates select="@*" />
     <xsl:for-each-group select="Source" group-by="@name">

Note, assuming you might have MapToType elements with different types within the grouped Source elements, you might then need a nested xsl:for-each-group within this one.

Try this XSLT....

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method ="xml" indent ="yes"/>

  <xsl:template match="Mappings">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:for-each-group select="Source" group-by="@name">
        <xsl:copy>
          <xsl:apply-templates select="@*" />
          <xsl:for-each-group select="current-group()/MapToType" group-by="@type">
            <xsl:copy>
             <xsl:apply-templates select="@*" />
             <xsl:apply-templates select="current-group()/MapTo" />
            </xsl:copy>
          </xsl:for-each-group>
        </xsl:copy>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

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

Alternatively, if the MapToType will always have the same type for each grouped Source element, you can simplify the XSLT to this..

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method ="xml" indent ="yes"/>

  <xsl:template match="Mappings">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:for-each-group select="Source" group-by="@name">
        <xsl:copy>
          <xsl:apply-templates select="@*" />
          <MapToType>
            <xsl:apply-templates select="MapToType/@*" />
            <xsl:apply-templates select="current-group()/MapToType/MapTo" />
          </MapToType>
        </xsl:copy>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

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

EDIT: If you are only using XSLT 1.0, then you need to use a technique called Muenchian Grouping. This will be overwhelming if you are new to XSLT, especially in this case if you have nested grouping which needs to take into account the parent.

Try this XSLT then:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method ="xml" indent ="yes"/>

  <xsl:key name="Sources" match="Source" use="@name" />
  <xsl:key name="MapToTypes" match="MapToType" use="concat(../@name, @type)" />

  <xsl:template match="Mappings">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:for-each select="Source[generate-id() = generate-id(key('Sources', @name)[1])]">
        <xsl:copy>
          <xsl:apply-templates select="@*" />
          <xsl:for-each select="key('Sources', @name)/MapToType[generate-id() = generate-id(key('MapToTypes', concat(../@name, @type))[1])]">
            <xsl:copy>
             <xsl:apply-templates select="@*" />
             <xsl:apply-templates select="key('MapToTypes', concat(../@name, @type))/MapTo" />
            </xsl:copy>
          </xsl:for-each>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

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

See it in action at http://xsltfiddle.liberty-development.net/jyRYYhX

Read up on Muenchian Grouping at http://www.jenitennison.com/xslt/grouping/muenchian.html. Then, read it again. And again....

Upvotes: 2

Related Questions