开发者

Group by and filter XML data with XSL

I have the following XML code:

<root>
    <options>
        <companies>
            <company url="http://www.brown.com">Brown LLC</company>
            <company url="http://www.yellow.com">Yellow LLC</company>
            <company url="http://www.black.com">Black LLC</company>
            <company url="http://www.bourdeaux.com">Bourdeaux LLC</company>
            <company url="http://www.orange.com">Orange LLC</company>           
        </companies>
    </options>
</root>

and I need to do two things with it:

  1. Build a html dropdown with the unique first letters found in the company nodes. Su开发者_开发技巧ch as:

    <select id="colors">
        <option value="B">B</option>
        <option value="O">O</option>
        <option value="Y">Y</option>
    </select>
    
  2. Build a secondary dropdown, which lists all the companies starting with a specific letter. Such as:

    <select id="companiesB">
        <option value="http://www.black.com">Black LLC</option>
        <option value="http://www.bordeaux.com">Bordeaux LLC</option>
        <option value="http://www.brown.com">Brown LLC</option>
    </select>
    

Any help would be appreciated!


First you need to define a key to 'group' all company elements together that share the same first letter

<xsl:key name="companyLetter" match="company" use="substring(text(), 1, 1)" />

Next, you would iterate over all company elements

<xsl:for-each select="options/companies/company">

However, you only want to process a company element if it is the first occurence of that element for its first letter. You do this by looking up the first element in your key for the first letter, and seeing if it is the same. Element comparison is done using the generate-id() function

<xsl:variable name="firstLetter" select="substring(text(), 1, 1)" />
<xsl:if test="generate-id(.) = generate-id(key('companyLetter', $firstLetter)[1])" >

Putting this altogether gives

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:key name="companyLetter" match="company" use="substring(text(), 1, 1)"/>
   <xsl:template match="/root">
      <select id="colors">
         <xsl:for-each select="options/companies/company">
            <xsl:sort select="text()"/>
            <xsl:variable name="firstLetter" select="substring(text(), 1, 1)"/>
            <xsl:if test="generate-id(.) = generate-id(key('companyLetter', $firstLetter)[1])">
               <option>
                  <xsl:attribute name="value">
                     <xsl:value-of select="$firstLetter"/>
                  </xsl:attribute>
                  <xsl:value-of select="$firstLetter"/>
               </option>
            </xsl:if>
         </xsl:for-each>
      </select>
   </xsl:template>
</xsl:stylesheet>

For the second drop-down, you can use a named template that gets passed a letter as a parameter. You can look up all elements for that letter using the same key as above.

<xsl:template name="Companies">
   <xsl:param name="firstLetter"/>
   <select>
      <xsl:attribute name="id">
         <xsl:value-of select="$firstLetter"/>
      </xsl:attribute>
      <xsl:for-each select="key('companyLetter', $firstLetter)">
         <xsl:sort select="text()"/>
         <option>
            <xsl:attribute name="value">
               <xsl:value-of select="@url"/>
            </xsl:attribute>
            <xsl:value-of select="text()"/>
         </option>
      </xsl:for-each>
   </select>
</xsl:template>

To call the template, it is simply a case of passing the required parameter, for example

<xsl:call-template name="Companies">
   <xsl:with-param name="firstLetter">B</xsl:with-param>
</xsl:call-template>

Of course, you could put this is an for-each loop if you wanted to show all drop-downs for all possible first-letters.


Here are two stylseheets, the first for producing the first select:

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

  <xsl:output method="html" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="k1" match="company" use="substring(., 1, 1)"/>

  <xsl:template match="companies">
    <select id="colors">
      <xsl:apply-templates select="company[generate-id() = generate-id(key('k1', substring(., 1, 1))[1])]">
        <xsl:sort select="substring(., 1, 1)" data-type="text"/>
      </xsl:apply-templates>
    </select>
  </xsl:template>

  <xsl:template match="company">
    <option value="{substring(., 1, 1)}">
      <xsl:value-of select="substring(., 1, 1)"/>
    </option>
  </xsl:template>

</xsl:stylesheet>

the second for producing the second select element:

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

  <xsl:param name="c" select="'B'"/>

  <xsl:output method="html" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="companies">
    <select id="companies{$c}">
      <xsl:apply-templates select="company[substring(., 1, 1) = $c]">
        <xsl:sort select="." data-type="text"/>
      </xsl:apply-templates>
    </select>
  </xsl:template>

  <xsl:template match="company">
    <option value="{@url}"><xsl:value-of select="."/></option>
  </xsl:template>

</xsl:stylesheet>


You should take a look into the Muenchian Method for grouping in XSLT.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜