Search code examples
xsltreportcrosstab

Transform a XML file using attribute values from one node and merging with another node


I have written below XSLT to achieve translation required for my reporting requirement.

Though, Input and Output XML are self-explanatory, but in short, requirement is to build a new XML from an existing XML by merging different pair of nodes, related by common attribute.

Input File

<?xml version="1.0" encoding="utf-8"?>
<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/"  xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<crosstab>
<values>
  <value row="R1" col="C1">111</value>
  <value row="R1" col="C2">222</value>
  <value row="R1" col="C3">333</value>
  <value row="R1" col="C4">444</value>
  <value row="R1" col="C5">555</value>
  <value row="R1" col="C6">666</value>
  <value row="R1" col="C7">777</value>
</values>
<corner>
  <caption>Number of Employees</caption>
</corner>
<columns>
  <colEdge>
    <caption>2010</caption>
    <colEdge id="C1">
      <caption>October</caption>
    </colEdge>
    <colEdge id="C2">
      <caption>November</caption>
    </colEdge>
    <colEdge id="C3">
      <caption>December</caption>
    </colEdge>
  </colEdge>
  <colEdge>
    <caption>2011</caption>
    <colEdge id="C4">
      <caption>January</caption>
    </colEdge>
    <colEdge id="C5">
      <caption>February</caption>
    </colEdge>
    <colEdge id="C6">
      <caption>March</caption>
    </colEdge>
    <colEdge id="C7">
      <caption>April</caption>
    </colEdge>
  </colEdge>
</columns>
<rows>
</rows>

Output File

<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns:c="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<crosstab>
<values>
  <value col="October 2010">111</value>
  <value col="November 2010">222</value>
  <value col="December 2010">333</value>
  <value col="January 2011">444</value>
  <value col="February 2011">555</value>
  <value col="March 2011">666</value>
  <value col="April 2011">777</value>
</values>
<corner>
  <caption>Number of Employees</caption>
</corner>
</crosstab>
</dataset>

Solution

  • Below is the XSLT that I used to achieve the required translation.

    XSLT File

    <?xml version="1.0"?>
    <xsl:stylesheet version="2.0" xmlns:c="http://developer.cognos.com/schemas/xmldata/1/"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <!-- Navigating crosstab node -->
    <xsl:template match="c:crosstab">
    <dataset>
      <crosstab>
        <xsl:apply-templates select="c:values" />
        <xsl:apply-templates select="c:corner" />
      </crosstab>
    </dataset>
    </xsl:template>
    <!-- Navigating values node -->
    <xsl:template match="c:values">
    <values>
      <xsl:apply-templates select="c:value" />
    </values>
    </xsl:template>
    <!-- Reading attributes of the value nodes -->
    <xsl:template match="c:value">
    <value>
      <xsl:apply-templates select="@col"/>
      <xsl:apply-templates/>
    </value>
    </xsl:template>
    <!-- Reading Month and Year nodes for the selected attribute -->
    <xsl:template match="@col">
     <xsl:attribute name="{name()}">
      <xsl:variable name="colAttribute" select="."/>
      <xsl:variable name="year" select="//c:colEdge[@id=$colAttribute[1]]/../*[1]/child::node()[1]"/>
      <xsl:variable name="month" select="//c:colEdge[@id=$colAttribute[1]]/child::node()[1]/child::node()[1]"/>
      <xsl:value-of select="concat($month,' ',$year)" />
    </xsl:attribute>
    </xsl:template>
    <!-- Reading attributes of the corner node -->
    <xsl:template match="c:corner">
    <corner>
      <caption>
        <xsl:value-of select="." />
      </caption>
    </corner>
    </xsl:template>
    </xsl:stylesheet>