CatchLight
CatchLight

Reputation: 5

How to combine multiple XSL into single XSLT trasnformation?

I have an input xml in which I have to group the elements, convert the Currency format and remove the namespace to add new namespace. I have created 3 XSLT program for each operation mentioned and the end result is fine if executed separately one after the other by feeding output of the first to second XSLT. However, I would like to combine all the XSLT templates into single XSLT transformation document. When combined, I am not getting the desired output xml, instead each node was added with namespace and grouping is going out of control without formatting the currency format. Sample XML is:

<?xml version="1.0" encoding="UTF-8"?>
<ns1:Root_Users xmlns:ns1="http://test.com/Users">
   <Users>
      <Id>111</Id>
      <Name>aaa</Name>
      <Division>HR</Division>
      <Salary>1000</Salary>
   </Users>
   <Users>
      <Id>222</Id>
      <Name>bbb</Name>
      <Division>FD</Division>
      <Salary>2000</Salary>
   </Users>
   <Users>
      <Id>333</Id>
      <Name>ccc</Name>
      <Division>HR</Division>
      <Salary>3000</Salary>
   </Users>
   <Users>
      <Id>444</Id>
      <Name>ddd</Name>
      <Division>FD</Division>
      <Salary>4000</Salary>
   </Users>
   <Users>
      <Id>555</Id>
      <Name>eee</Name>
      <Division>IT</Division>
      <Salary>5000</Salary>
   </Users>
</ns1:Root_Users>

I have used following XSLT transform to achieve the output.

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

  <!-- Grouping the Users for each Division -->
  <xsl:key name="GroupKey" match="Users" use="Division"/>
  <xsl:template match="/*">
        <Root_Users>
            <xsl:apply-templates/>
        </Root_Users>
  </xsl:template>
  <xsl:template match="Users[generate-id()=generate-id(key('GroupKey',Division)[1])]">
        <Department name="{Division}">
            <xsl:copy-of select="key('GroupKey',Division)"/>
        </Department>
  </xsl:template>
  <xsl:template match="Users[not(generate-id()=generate-id(key('GroupKey',Division)[1]))]"/>

  <!-- Converting Salary to Denmark Currency Format -->
  <xsl:decimal-format name="DenmarkCurrencyFormat" grouping-separator="." decimal-separator=","/>
  <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
  </xsl:template>
  <xsl:template match="//Salary">
        <xsl:copy>
            <xsl:value-of select="format-number(., 'kr ###.###.###,00', 'DenmarkCurrencyFormat')"/>
        </xsl:copy>
  </xsl:template>


  <!-- Removing and Adding new Namespace to Root -->  
  <xsl:template match="/*">
    <xsl:element name="{local-name()}" namespace="http://test.com/Department">
            <xsl:apply-templates select="@* | node()" />
        </xsl:element>
  </xsl:template>
  <xsl:template match="@*">
        <xsl:attribute name="{local-name()}">
            <xsl:value-of select="." />
        </xsl:attribute>
  </xsl:template>
  <xsl:template match="text() | comment() | processing-instruction()">
        <xsl:copy />
  </xsl:template>

</xsl:stylesheet>

When applied to the input xml, I am getting following output xml:

<Root_Users xmlns="http://test.com/Department">
    <Department xmlns="" name="HR">
        <Users xmlns:ns1="http://test.com/Users">
            <Id>111</Id>
            <Name>aaa</Name>
            <Division>HR</Division>
            <Salary>1000</Salary>
        </Users>
        <Users xmlns:ns1="http://test.com/Users">
            <Id>333</Id>
            <Name>ccc</Name>
            <Division>HR</Division>
            <Salary>3000</Salary>
        </Users>
    </Department>
    <Department xmlns="" name="FD">
        <Users xmlns:ns1="http://test.com/Users">
            <Id>222</Id>
            <Name>bbb</Name>
            <Division>FD</Division>
            <Salary>2000</Salary>
        </Users>
        <Users xmlns:ns1="http://test.com/Users">
            <Id>444</Id>
            <Name>ddd</Name>
            <Division>FD</Division>
            <Salary>4000</Salary>
        </Users>
    </Department>
    <Department xmlns="" name="IT">
        <Users xmlns:ns1="http://test.com/Users">
            <Id>555</Id>
            <Name>eee</Name>
            <Division>IT</Division>
            <Salary>5000</Salary>
        </Users>
    </Department>
</Root_Users>

But the desired output is as mentioned below.

<Root_Users xmlns="http://test.com/Department">
    <Department name="HR">
        <Users>
            <Id>111</Id>
            <Name>aaa</Name>
            <Division>HR</Division>
            <Salary>kr 1.000,00</Salary>
        </Users>
        <Users>
            <Id>333</Id>
            <Name>ccc</Name>
            <Division>HR</Division>
            <Salary>kr 3.000,00</Salary>
        </Users>
    </Department>
    <Department name="FD">
        <Users>
            <Id>222</Id>
            <Name>bbb</Name>
            <Division>FD</Division>
            <Salary>kr 2.000,00</Salary>
        </Users>
        <Users>
            <Id>444</Id>
            <Name>ddd</Name>
            <Division>FD</Division>
            <Salary>kr 4.000,00</Salary>
        </Users>
    </Department>
    <Department name="IT">
        <Users>
            <Id>555</Id>
            <Name>eee</Name>
            <Division>IT</Division>
            <Salary>kr 5.000,00</Salary>
        </Users>
    </Department>
</Root_Users>

Please help me to get the desired xml. I am very close but not able to find what exactly causing the issue. Much appreciated for your pointers.

Upvotes: 0

Views: 219

Answers (1)

ABach
ABach

Reputation: 3738

Your solution was on the right track - 2 primary things I noticed:

  1. I got around the problem of extraneous namespaces via the second-to-last template in my solution below; there are other ways to do it, but that's what came to my brain tonight. :)
  2. Note that the <xsl:decimal-format> instruction needs to be at the top level.

When this XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://test.com/Department" 
  version="1.0">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:decimal-format
    name="DenmarkCurrencyFormat"
    grouping-separator="."
    decimal-separator=","/>

  <xsl:key
    name="kUserByDivision"
    match="Users"
    use="Division"/>

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

  <xsl:template match="/*">
    <Root_Users>
      <xsl:apply-templates
        select="*[generate-id() = 
                  generate-id(key('kUserByDivision', Division)[1])]"/>
    </Root_Users>
  </xsl:template>

  <xsl:template match="Users">
    <Department name="{Division}">
      <xsl:for-each select="key('kUserByDivision', Division)">
        <Users>
          <xsl:apply-templates/>
        </Users>
      </xsl:for-each>
    </Department>
  </xsl:template>

  <xsl:template match="Users/*">
    <xsl:element name="{name()}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="Salary/text()">
    <xsl:value-of
      select="concat('kr ',
                     format-number(.,
                                   '###.###,00',
                                   'DenmarkCurrencyFormat'))"/>
  </xsl:template>
</xsl:stylesheet>

...is applied against the provided XML:

<?xml version="1.0" encoding="UTF-8"?>
<ns1:Root_Users xmlns:ns1="http://test.com/Users">
  <Users>
    <Id>111</Id>
    <Name>aaa</Name>
    <Division>HR</Division>
    <Salary>1000</Salary>
  </Users>
  <Users>
    <Id>222</Id>
    <Name>bbb</Name>
    <Division>FD</Division>
    <Salary>2000</Salary>
  </Users>
  <Users>
    <Id>333</Id>
    <Name>ccc</Name>
    <Division>HR</Division>
    <Salary>3000</Salary>
  </Users>
  <Users>
    <Id>444</Id>
    <Name>ddd</Name>
    <Division>FD</Division>
    <Salary>4000</Salary>
  </Users>
  <Users>
    <Id>555</Id>
    <Name>eee</Name>
    <Division>IT</Division>
    <Salary>5000</Salary>
  </Users>
</ns1:Root_Users>

...the wanted result is produced:

<Root_Users xmlns="http://test.com/Department">
  <Department name="HR">
    <Users>
      <Id>111</Id>
      <Name>aaa</Name>
      <Division>HR</Division>
      <Salary>kr 1.000,00</Salary>
    </Users>
    <Users>
      <Id>333</Id>
      <Name>ccc</Name>
      <Division>HR</Division>
      <Salary>kr 3.000,00</Salary>
    </Users>
  </Department>
  <Department name="FD">
    <Users>
      <Id>222</Id>
      <Name>bbb</Name>
      <Division>FD</Division>
      <Salary>kr 2.000,00</Salary>
    </Users>
    <Users>
      <Id>444</Id>
      <Name>ddd</Name>
      <Division>FD</Division>
      <Salary>kr 4.000,00</Salary>
    </Users>
  </Department>
  <Department name="IT">
    <Users>
      <Id>555</Id>
      <Name>eee</Name>
      <Division>IT</Division>
      <Salary>kr 5.000,00</Salary>
    </Users>
  </Department>
</Root_Users>

Upvotes: 1

Related Questions