jeudi 17 juillet 2003

XSL:for-each-group: a starter

Have you ever encoutered a flat XML file? 
I know it sounds a little bit weird, but they exist; even more, they are very commons. 
In fact, they could be seen as an XML-isation of a trivial flat file (think csv file) or a single RDMS table (I do not mean it is a best practice in the later cases) 
But where I find them very useful is for resources/properties/configuration files (or whatever you call them!): they are files generally edited by hand which contain every single parameter your application mandates for running. 
The flatness comes from the absence of hierarchy: in french, we would say it is a "rateau" (a rake). Here is an exemple of such a rake-file ;-)

<Resources>
<user name="Gosling" firstname="James" company="Sun" group="guru"/>
<user name="Kay" firstname="Michael" company="SoftwareAG" group="guru"/>
<user name="Poirier" firstname="Charles-Antoine" company="WorldCompany" group="beginner"/>
</Resources>

From such a file, very often comes the need to get a hierarchical view; by instance, one focused on 'company' or 'group'.

That hierarchical view could be needed for producing another XML file, sending a stream over the wire or get an in-memory tree-view of those resources.

An example of such an hierarchical perspective could be as follow (I apologize for being too much 'hierarchical' in that example ;-)):
 
<Groups>
<Group type="guru">
<Member fromCompany="Sun">
<FirstName>James</FirstName>
<LastName>Gosling</LastName>
<Member>
<Member fromCompany="SofwareAG">
<FirstName>Michael</FirstName>
<LastName>Kay</LastName>
<Member>
</Group>
<Group type="beginner">
<Member fromCompany="WorldCompany">
<FirstName>Charles-Antoine</FirstName>
<LastName>Poirier</LastName>
<Member>
</Group>
</Groups>


Obviously to go from <Resources>-XML to <Groups>-XML, you are already thinking to ... XSLT.

To be more precise, you certainly envision XSLT 2.0 which contains a very rich new feature: xsl:for-each-group. It enables you to get something similar to SQL-'Group-By'.

As an example, here the XSLT which allow the aforementioned transformation:
 
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent='yes' method='xml' encoding="ISO-8859-1" />

<xsl:template match="/">
<Groups>
<!-- "sort" 'user' by 'group' -->
<xsl:for-each-group select="//user" group-by="@group"> <!-- group-by group is not a joke!!! -->
<xsl:element name="Group"> <!-- Creates a new 'Group' element as needed -->
<xsl:attribute name="type"><xsl:value-of select="@group"/></xsl:attribute>

<xsl:for-each select="current-group()"><!-- Iterate over the XSL-group to create 'Member's -->
<xsl:element name="Member">
<xsl:attribute name="fromCompany"><xsl:value-of select="@company"/></xsl:attribute>
<FirstName><xsl:value-of select="@firstname"/></FirstName>
<LastName><xsl:value-of select="@name"/></LastName>
</xsl:element>
</xsl:for-each>

</xsl:element>
</xsl:for-each-group>
</Groups>
</xsl:template>
</xsl:transform>


This is a very simple case where you can take advantage of this wonderful new feature (using Saxon makes it rock!). It leads to very elegant (and readable) XSLT code (and that's not the least)

Aucun commentaire: