Question

I need to transform below input XML to output XML. Can you please provide any ideas for this.

I need to transform below input XML to output XML. Can you please provide any ideas for this.

Input XML :

<GenericCollection ParamValue="ParamValue1" xmlns="http://www.example.org">
  <Generic>
    <store>Store1</store>
    <metricName>Metric1</metricName>
    <metricValue>1</metricValue>
  </Generic>
  <Generic>
    <store>Store1</store>
    <metricName>Metric2</metricName>
    <metricValue>1</metricValue>
  </Generic>
  <Generic>
    <store>Store2</store>
    <metricName>Metric1</metricName>
    <metricValue>1</metricValue>
  </Generic>
  <Generic>
    <store>Store2</store>
    <metricName>Metric1</metricName>
    <metricValue>2</metricValue>
  </Generic>
  <Generic>
    <store>Store2</store>
    <metricName>Metric2</metricName>
    <metricValue>1</metricValue>
  </Generic>
  <Generic>
    <store>Store3</store>
    <metricName>Metric1</metricName>
    <metricValue>1</metricValue>
  </Generic>
  <Generic>
    <store>Store3</store>
    <metricName>Metric1</metricName>
    <metricValue>2</metricValue>
  </Generic>
</GenericCollection>

output:

<?xml version = '1.0' encoding = 'UTF-8'?>
<ns4:EnterpriseDocument>
  <ns4:DataSet>
    <ns4:Dimension ref_name="bu_code" value="Store1">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
      </ns4:Metric>
      <ns4:Metric ref_name="Metric2">
        <ns4:Data value="1"/>
      </ns4:Metric>
    </ns4:Dimension>
    <ns4:Dimension ref_name="bu_code" value="Store2">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
        <ns4:Data value="2"/>            
      </ns4:Metric>
      <ns4:Metric ref_name="Metric2">
        <ns4:Data value="1"/>
      </ns4:Metric>
    </ns4:Dimension>
    <ns4:Dimension ref_name="bu_code" value="Store3">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
        <ns4:Data value="2"/>            
      </ns4:Metric>
    </ns4:Dimension>     
  </ns4:DataSet>
</ns4:EnterpriseDocument>
    Thanks,
    Ramesh
Was it helpful?

Solution

If you are able to use XSLT 2.0, then you should probably be using xsl:for-each-group here, rather than xsl:for-each because you are trying to group the elements here.

Firstly you are grouping by Generic elements by their store value, so you would write this.

<xsl:for-each-group select="Generic" group-by="store">

Then within this, you would output the *Dimension * element for the group like so:

<Dimension ref_name="bu_code" value="{current-grouping-key()}">

Next, you want to further group the elements in this group by their metricName value

<xsl:for-each-group select="current-group()" group-by="metricName">

Note the use of current-group() here. You are only iterating over the elements in the current "store" group, and not over all generic elements.

Finally, to output all your Data elements, you would do this

<xsl:for-each select="current-group()">

Try the following XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xpath-default-namespace="http://www.example.org">

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

<xsl:template match="/*">
   <EnterpriseDocument>
   <DataSet>
      <xsl:for-each-group select="Generic" group-by="store">
         <Dimension ref_name="bu_code" value="{current-grouping-key()}">
            <xsl:for-each-group select="current-group()" group-by="metricName">
               <Metric ref_name="{current-grouping-key()}">
                  <xsl:for-each select="current-group()">
                    <Data value="{metricValue}"/>
                  </xsl:for-each>
               </Metric>
            </xsl:for-each-group>
         </Dimension>
      </xsl:for-each-group>
    </DataSet>
   </EnterpriseDocument>
</xsl:template>

</xsl:stylesheet>

When run on your XML sample, the following is output (Note, in my sample I am not including any namespaces, just for clarity)

<EnterpriseDocument>
   <DataSet>
      <Dimension ref_name="bu_code" value="Store1">
         <Metric ref_name="Metric1">
            <Data value="1"/>
         </Metric>
         <Metric ref_name="Metric2">
            <Data value="1"/>
         </Metric>
      </Dimension>
      <Dimension ref_name="bu_code" value="Store2">
         <Metric ref_name="Metric1">
            <Data value="1"/>
            <Data value="2"/>
         </Metric>
         <Metric ref_name="Metric2">
            <Data value="1"/>
         </Metric>
      </Dimension>
      <Dimension ref_name="bu_code" value="Store3">
         <Metric ref_name="Metric1">
            <Data value="1"/>
            <Data value="2"/>
         </Metric>
      </Dimension>
   </DataSet>
</EnterpriseDocument>

OTHER TIPS

And here a xslt-1.0 solution. Which does the grouping based on xsl:key (Muenchian Method

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:e="http://www.example.org"
                xmlns:ns4="http://www.exampl.ns4.org">

    <xsl:strip-space elements="*" />
    <xsl:output method="xml" indent="yes"/>

    <xsl:key name="kStore" match="e:Generic/e:store"  use="."/>
    <xsl:key name="kStoreMetric" match="e:Generic/e:metricName"  use="concat(../e:store,'|', .)"/>

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

    <xsl:template match="e:GenericCollection">
        <ns4:EnterpriseDocument>
        <xsl:for-each select="//e:Generic/e:store[
             generate-id() = generate-id( key('kStore', . ) [1])
              ]" >
            <ns4:DataSet>
                <ns4:Dimension ref_name="bu_code" value="{.}">
                    <xsl:variable name="s" select="." />
                    <xsl:for-each select="//e:metricName[
                            generate-id() = generate-id( key('kStoreMetric', concat($s,'|', .) ) [1])
                            ]" >
                        <ns4:Metric ref_name="{.}">
                            <xsl:variable name="m" select="."/>
                            <xsl:for-each select="key('kStoreMetric', concat($s,'|', $m) )" >
                                <ns4:Data value="{../e:metricValue}"/>
                            </xsl:for-each>
                        </ns4:Metric>
                    </xsl:for-each>
                </ns4:Dimension>
            </ns4:DataSet>
            </xsl:for-each>
        </ns4:EnterpriseDocument>
    </xsl:template>

</xsl:stylesheet>

Which will generate the following output:

<ns4:EnterpriseDocument xmlns:ns4="http://www.exampl.ns4.org" xmlns:e="http://www.example.org">
  <ns4:DataSet>
    <ns4:Dimension ref_name="bu_code" value="Store1">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
      </ns4:Metric>
      <ns4:Metric ref_name="Metric2">
        <ns4:Data value="1"/>
      </ns4:Metric>
    </ns4:Dimension>
  </ns4:DataSet>
  <ns4:DataSet>
    <ns4:Dimension ref_name="bu_code" value="Store2">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
        <ns4:Data value="2"/>
      </ns4:Metric>
      <ns4:Metric ref_name="Metric2">
        <ns4:Data value="1"/>
      </ns4:Metric>
    </ns4:Dimension>
  </ns4:DataSet>
  <ns4:DataSet>
    <ns4:Dimension ref_name="bu_code" value="Store3">
      <ns4:Metric ref_name="Metric1">
        <ns4:Data value="1"/>
        <ns4:Data value="2"/>
      </ns4:Metric>
    </ns4:Dimension>
  </ns4:DataSet>
</ns4:EnterpriseDocument>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top