Posted on September 12th, 2007

Today’s post comes from a tutorial I’m working on for a customer who still does a lot of XML/XSL stuff. One of the more challenging things to do in XSL1.0 (which is what most browsers support) was getting a distinct list of values from a set of nodes. Exslt has a good article on the subject, including this link to Jeni Tenison’s XSLT Template. In the back of my mind, I kept wondering if there was some way to do this with keys, and the customer I’m working with showed a way they do it.

Let’s say you have an XML document like:


 

    FL
 

 

    GA
 

 

    MN
 

 

    FL
 

And you want to output something like:


  FL
  GA
  MN

The way to do this with keys is to define an xsl:key like this:

This let’s us set up a key to access Address nodes with. We can then get the distinct list in an xsl:for-each node by combining this with the generate-key function:

generate-id “generates a key that uniquely identifies a specified node“, in this case, basically creating a node-set of the state nodes and returning the first node for each distinct value. So our full xsl would look something like:





 


Which is all well and good. However, in the tutorial example, state isn’t in its own node – it’s embedded in the Address like:


123 Sample Way, Tampa, FL

Which is a tad trickier. In the tutorial, we’ve allowed the assumption that the state will always be the last 2 characters of the Address field. So how can we get a distinct list of states with data like this?

Turns out that we can do it in a very similar (if complex) way. We start off by specifying the key:

Here, our Address node is a child of Customer, which is a child of Customers – the root node. So we are matching Customers/Customer, and using the value of the last 2 characters of the Address. We then need to do the same for our for-each loop:


 
   
      
name=”state”
       select=”substring(Address, (string-length(Address)-1))”/>
  

So we are doing the unique select on the state value. However, this returns the matching node – which is an Address node, so to use the value (in our example here as a parameter to a named template) we have to still substring it out.

Of course, this would probably be a better time to either see if you can get State into its own node, or do some sort of pre-processing to do that, but when you have neither of those options, this will work.

Thanks to Len and the SR team for the initial key idea

6 Responses to “Getting a distinct list of values in XSL from a substring of data in a node”

  1. Anonymous says:

    Or you could just put a bullet in your head.

  2. Nicky says:

    pls put ] at the end of xsl:for-each
    before closing xsl:for-each
    Else everything workig fine.
    Thanks a lot.
    Karishma

  3. Anonymous says:

    A whole year after this post, and the information is very helpful. Thanks very much.

    Carl.

  4. Anonymous says:

    two years, still helpful

  5. Joe Schmoe says:

    I agree with the first commenter. Seems like that would be much quicker and less painful.

  6. San says:

    I am looking for some thing on this lines, but the code lines are missing, can you please update it.

    Cheers,
    San

Leave a Reply