XSLT Extensions
 

In this page, I'm compiling some examples for XSLT extensions (which allows us to extend the XSLT language).
 

1. Image handling using XSLT

The following question was asked on XSL-List.

> Has anyone written an XSLT 1.0 extension function to get the
> height and width of an image at a specified URL?

Following is an example of Java extension function (tested with JDK 1.6.0) for Xalan-J:

ImageHelper.java
------------------

import java.net.URL;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

public class ImageHelper {

    public static int getHeight(String imgUrl) {
       
        int height = -1;

        try {
            URL url = new URL(imgUrl);
            BufferedImage buffImg = ImageIO.read(url);
            height = buffImg.getHeight();
        }
        catch(java.net.MalformedURLException ex) {
            ex.printStackTrace();
        }
        catch(java.io.IOException ex) {
            ex.printStackTrace();
        }

        return height;
    }

    public static int getWidth(String imgUrl) {

        int width = -1;

        try {
            URL url = new URL(imgUrl);
            BufferedImage buffImg = ImageIO.read(url);
            width = buffImg.getWidth();
        }
        catch(java.net.MalformedURLException ex) {
            ex.printStackTrace();
        }
        catch(java.io.IOException ex) {
            ex.printStackTrace();
        }

        return width;
    }

}


Following is the test stylesheet (named, test.xsl):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:java="http://xml.apache.org/xalan/java" version="1.0">

<xsl:output method="text" />

<xsl:template match="/">
    <xsl:variable name="imageUrl" select="'http://www.google.co.in/intl/en/images/about_logo.gif'" />
    <xsl:variable name="imgHeight" select="java:ImageHelper.getHeight($imageUrl)" />
    <xsl:variable name="imgWidth" select="java:ImageHelper.getWidth($imageUrl)" />

    Height - <xsl:value-of select="$imgHeight" /><xsl:text>&#xa;</xsl:text>
    Width - <xsl:value-of select="$imgWidth" />
</xsl:template>

</xsl:stylesheet>

When Xalan-J is run as following:
java org.apache.xalan.xslt.Process -in test.xsl -xsl test.xsl

The output produced is:
Height - 65
Width - 175


2.
Grouping and numbering

The following question was asked on XSL-List.

I need to run through a large structure that resembles:

<sample>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>300</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
</sample>

Firstly, I need to sort the data into <group_id> order, so I use:
<xsl:apply-templates select="sample//result/details">
    <xsl:sort select="group_id" data-type="number" />
</xsl:apply-templates>

Next, within the called template, I need to loop through all the <details> and add row numbering information but add an extra row at the end of each group.

So hope to see:
<output row="1">250</output>
<output row="2">250</output>
<output row="3" />
<output row="4">300</output>

I tried to solve this problem as following:

Please consider the following example.

Java extension class:

public class Iterator {
    private static int i = 0;

    public static int next() {
        i++;
        return i;
    }
}


XSLT stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                       xmlns:java="http://xml.apache.org/xalan/java"
                       exclude-result-prefixes="java"
                       version="1.0">

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

<xsl:key name="by-group" match="result" use="details/group_id" />

<xsl:template match="sample">
    <sample>
        <xsl:for-each select="result[generate-id() = generate-id(key('by-group', details/group_id)[1])]">
            <xsl:sort select="details/group_id" data-type="number" />
            <xsl:variable name="x" select="position()" />
            <xsl:for-each select="key('by-group', details/group_id)">
                <output row="{java:Iterator.next()}"><xsl:value-of select="details/group_id" /></output>
            </xsl:for-each>
            <xsl:if test="$x != last()">
                <output row="{java:Iterator.next()}" />
            </xsl:if>
        </xsl:for-each>
    </sample>
</xsl:template>

</xsl:stylesheet>

Using Xalan-J (ver 2.7.0), when the above stylesheet is applied to the XML:

<sample>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>300</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
</sample>

The output produced is:

<?xml version="1.0" encoding="UTF-8"?>
<sample>
    <output row="1">250</output>
    <output row="2">250</output>
    <output row="3"/>
    <output row="4">300</output>
</sample>

David Carlisle provided a nice solution, written in pure XSLT 1.0:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
    <xsl:key name="g" match="group_id" use="." />
    <xsl:output indent="yes"/>

    <xsl:variable name="g" select="/sample/result/details/group_id[generate-id()=generate-id(key('g',.))]"/>

    <xsl:template match="sample">
        <xsl:for-each select="$g">
            <xsl:sort select="."/>
            <xsl:variable name="p" select="position()-1"/>
            <xsl:variable name="c" select="count(key('g',$g[.&lt;current()]))"/>
            <xsl:for-each select="key('g',.)">
                <output row="{$c+$p+position()}"><xsl:value-of select="."/></output>
            </xsl:for-each>
            <xsl:if test="position()!=last()">
                <output row="{$c+count(key('g',.))+$p+1}"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>



Home


Last Updated: Apr 24, 2010