Sunday, August 13, 2006

Interesting XSLT Problem

This weekend I worked on a very interesting XSLT problem. I had to convert an XML like:



<?xml version="1.0" encoding="UTF-8"?>
<category>
    <type>1</type>
    <value>20</value>
</category>
<category>
    <type>2</type>
    <value>10</value>
</category>
<category>
    <type>1</type>
    <value>25</value>
</category>
<category>
    <type>2</type>
    <value>40</value>
</category>
<category>
    <type>2</type>
    <value>41</value>
</category>
</categories>


to something like:


<?xml version="1.0" encoding="UTF-8"?>
<output>
    <grid>
        <type val="1">
            <value>20</value>
            <value>-NA-</value>
            <value>25</value>
            <value>-NA-</value>
            <value>-NA-</value>
    </type>
        <type val="2">
            <value>-NA-</value>
            <value>10</value>
            <value>-NA-</value>
            <value>40</value>
            <value>41</value>
        </type>
    </grid>
</output>

I spent couple of hours figuring out how to solve it. Then I came across an interesting XPATH statement at http://www.topxml.com/code/default.asp?p=3&id=v20040207215613&ms=100&l=&sw=All to retrieve distinc values. I worked upon it and wrote the following XSLT which strangely worked :)



<?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" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <Output>
            <Grid>
                <xsl:for-each select="//category[not(type=preceding::category/type)]/type">
                    <xsl:element name="type">
                        <xsl:attribute name="val"><xsl:value-of select="."/></xsl:attribute>
                        <xsl:call-template name="map">
                            <xsl:with-param name="type">
                                <xsl:value-of select="."/>
                            </xsl:with-param>
                        </xsl:call-template>
                    </xsl:element>
                </xsl:for-each>
            </Grid>
        </Output>
    </xsl:template>
    <!--Called Once for each type -->
    <xsl:template name="map">
        <xsl:param name="type"/>
        <xsl:for-each select="//category[not(value=preceding::category/value)]/value">
            <xsl:call-template name="checkAvailability">
                <xsl:with-param name="type">
                    <xsl:value-of select="$type"/>
                </xsl:with-param>
                <xsl:with-param name="value">
                    <xsl:value-of select="."/>
                </xsl:with-param>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    <!-- Called once for each value -->
    <xsl:template name="checkAvailability">
        <xsl:param name="type"/>
        <xsl:param name="value"/>
        <value>
            <xsl:choose>
                <xsl:when test="//category[type=$type and value=$value]">
                    <xsl:value-of select="$value"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:text>-NA-</xsl:text>
                </xsl:otherwise>
            </xsl:choose>
        </value>
    </xsl:template>
</xsl:stylesheet>

No comments:

Post a Comment