Reputation: 547
I have a xml which is like:
<bookstores>
<bookstore>
<book id="1">
<author>ABC</author>
</book>
<book id="2">
<title>YYY</title>
</book>
</bookstore>
<bookstore>
<book id="3">
<author>ABC</author>
</book>
<book id="4">
<author>DEF</author>
</book>
</bookstore>
<bookstore>
<book id="5">
<price>50</price>
</book>
<book id="6">
<title>ZZZ</title>
</book>
</bookstore>
</bookstores>
I would like to select the first occurrence of the child of 'book' node, or in other words, all unique child node of the 'book' node.
So the output should be like:
author
title
price
I wrote a xslt as:
<xsl:for-each select="bookstores/bookstore/book">
<xsl:if test="count(preceding-sibling::*[1]) = 0">
<xsl:value-of select="local-name(*[1])"/>
</xsl:if>
</xsl:for-each>
It returned me nothing...Could anyone give me some help on this? Thanks!!
UPDATE:
What if I have several 'bookstores' elements in my xml, and I just would like to limit the uniqueness within the context of each 'bookstores' so that even 'author' appears in one 'bookstores', it could still be displayed if it appears in another 'bookstores'?
Upvotes: 5
Views: 9808
Reputation: 243449
Somewhat shorter/simpler -- completely in "push style":
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kBChildrenByName" match="book/*" use="name()"/>
<xsl:template match=
"book/*[generate-id()=generate-id(key('kBChildrenByName', name())[1])]">
<xsl:value-of select="concat(name(), '
')"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<bookstores>
<bookstore>
<book id="1">
<author>ABC</author>
</book>
<book id="2">
<title>YYY</title>
</book>
</bookstore>
<bookstore>
<book id="3">
<author>ABC</author>
</book>
<book id="4">
<author>DEF</author>
</book>
</bookstore>
<bookstore>
<book id="5">
<price>50</price>
</book>
<book id="6">
<title>ZZZ</title>
</book>
</bookstore>
</bookstores>
the wanted, correct result is produced:
author
title
price
Explanation:
Appropriate use of the Muenchian grouping method.
Upvotes: 3
Reputation: 70618
If you are using XSLT1.0, the way to get distinct elements is by a technique called Muenchian Grouping. In your case you want to 'group' by book child elements, so to start with, you define a key to look up the child elements of books by the element name
<xsl:key name="child" match="book/*" use="local-name()" />
To get the distinct names, you then look at all book child elements, but only output the elements that occur first in the group for their given name. You do this using this scary statement:
<xsl:apply-templates
select="//book/*[generate-id() = generate-id(key('child', local-name())[1])]" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="child" match="book/*" use="local-name()" />
<xsl:template match="/">
<xsl:apply-templates select="//book/*[generate-id() = generate-id(key('child', local-name())[1])]" />
</xsl:template>
<xsl:template match="//book/*">
<xsl:value-of select="concat(local-name(), ' ')" />
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following is output
author
title
price
Upvotes: 3
Reputation: 1753
You can use
<xsl:for-each select="//book">
to select occurances
Upvotes: -1