Re: [xsl] Re-Organize and Sort Source Tree (Muenchian Method)

Subject: Re: [xsl] Re-Organize and Sort Source Tree (Muenchian Method)
From: Anton Triest <anton@xxxxxxxx>
Date: Wed, 29 Sep 2004 21:53:56 +0200
Hi Ethan,

Here's a stylesheet that produces the desired output, by using the identity transform
to copy all elements, and additional templates to match the elements you want to change:


<?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:strip-space elements="*"/>

   <!-- keys by city name -->
   <xsl:key name="cities" match="city" use="."/>
   <xsl:key name="offices" match="office" use="cities/city"/>

   <!-- identity transform: copy all elements -->
   <xsl:template match="*">
       <xsl:copy>
           <xsl:copy-of select="@*"/>
           <xsl:apply-templates/>
       </xsl:copy>
   </xsl:template>

   <!-- sort divisions by @id -->
   <xsl:template match="divisions">
       <divisions>
           <xsl:apply-templates select="division">
               <xsl:sort select="@id"/>
           </xsl:apply-templates>
       </divisions>
   </xsl:template>

   <!-- sort regions and countries by @name -->
   <xsl:template match="regions|countries">
       <xsl:copy>
           <xsl:apply-templates select="region|country">
               <xsl:sort select="@name"/>
           </xsl:apply-templates>
       </xsl:copy>
   </xsl:template>

<!-- reorganize 'country' elements -->
<xsl:template match="country">
<country>
<xsl:copy-of select="@*"/>
<cities>
<!-- group 'city' elements -->
<xsl:apply-templates select="offices/office/cities/city[count(.|key('cities',.)[1])=1]">
<xsl:sort select="."/>
</xsl:apply-templates>
</cities>
</country>
</xsl:template>


   <!-- new 'city' elements -->
   <xsl:template match="city">
       <city>
           <name><xsl:value-of select="."/></name>
           <offices>
               <xsl:apply-templates select="key('offices',.)">
                   <xsl:sort select="name"/>
               </xsl:apply-templates>
           </offices>
       </city>
   </xsl:template>

   <!-- new office elements -->
   <xsl:template match="office">
       <office>
           <xsl:copy-of select="@*"/>
           <!-- copy all children except 'cities' -->
           <xsl:apply-templates select="*[local-name()!='cities']"/>
       </office>
   </xsl:template>

</xsl:stylesheet>

Note however, the Muenchian grouping will only work properly if there are no cities
with the same name in different countries (the 'cities' key matches all city elements
throughout the document). If that's a problem you'll have to include additional
information in the key's 'use' value, something like:


<xsl:key name="cities" match="city" use="concat(ancestor::country/@name,'-',.)"/>

and then of course, use the same test in the Muenchian grouping predicate:

[count(.|key('cities',concat(ancestor::country/@name,'-',.))[1])=1]

Cheers,
Anton

Current Thread