tags:

views:

395

answers:

2

Hi. I have the following XML:

<a>
    <b>
        <d>D1 content (can include child nodes)</d>
    </b>
    <b>
        <c>C1 content (can include child nodes)</c>
    </b>
    <b>
        <e>E1 content (can include child nodes)</e>
    </b>
    <b>
        <c>C2 content (can include child nodes)</c>
    </b>
</a>

Using XSLT 1.0, I need to produce from this simply: "cde"; i.e. a distinct list of the names of the immediate children of /a/b/ ordered by the node name. Each b has exactly one child of arbitrary name.

I can produce "ccde":

<xsl:for-each select="/a/b/*">
    <xsl:sort select="name(.)"/>
    <xsl:value-of select="name(.)" />
</xsl:for-each>

I've tried using the usual preceding-sibling:: comparison, but as each b only has one child, the preceding sibling is always nothing.

+1  A: 

First add this key element to the top of your XSL:-

<xsl:key name="tagNames" match="/a/b/*" use="name()" />

Now your for each loop can look like this:-

<xsl:template match="/*">
 <xsl:for-each select="/a/b/*[count(. | key('tagNames', name())[1]) = 1]">
  <xsl:sort select="name()" />
  <xsl:value-of select="name()" />
 </xsl:for-each>
</xsl:template>
AnthonyWJones
Thanks - works well.
sellotape
A: 

Hi!

You can use Muenchian method:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:key name="groupIndex" match="*" use="name()" />
    <xsl:template match="/">
     <xsl:apply-templates select="a/b"/>
    </xsl:template>
    <xsl:template match="b">
     <xsl:apply-templates select="*[1][generate-id(.) = generate-id(key('groupIndex', name())[1])]" mode="group" />
    </xsl:template>
    <xsl:template match="*" mode="group">
     <xsl:value-of select="name()"/>
    </xsl:template>
</xsl:stylesheet>
Goran
Thanks. My example was obviously simplified - would this work just as well (with the * matches in there) when my XML is a small part of a larger document?
sellotape
This probably works in the posted simplified case but there are scenerios in which this will fail as a result of the key element matching __all__ elements in the document rather than just the children of /a/b.
AnthonyWJones
Agreed. The intention was to show the concept (and make it work on example). The code should be modified to fit the real life scenario (ie. using /a/b* as key). I'm just partial to using templates vs. for-each construct whenever possible.
Goran