ABC

How to select the first occurrence of a node using xslt

I have an xml that looks 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 element of the book book node, or, in other words, all the unique child elements of the node in the book book node.

So the output should look like this:

 author title price 

I wrote 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> 

He gave me nothing ... Can anyone help me? Thanks!!

UPDATE:

What if I have several elements of “bookstores” in my xml and I just want to limit the uniqueness in the context of each “bookstore”, so that even the “author” appears in one “bookstore”, he can still be displayed if it appears in other "bookstores"?

+4
source share
3 answers

If you are using XSLT1.0, the way to get individual elements is a method called Muenchian Grouping. In your case, you want to "group" by elements of the child elements of the book, so for starters you define a key to search for child elements of books by the name of the element

  <xsl:key name="child" match="book/*" use="local-name()" /> 

To get individual names, you then look at all the child elements of the book, but only display the elements that appear first in the group for their 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(), '&#10;')" /> </xsl:template> </xsl:stylesheet> 

When applied to your XML, the following is output:

 author title price 
+3
source

Somewhat shorter / simpler - completely 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(), '&#xA;')"/> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet> 

When this conversion 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 desired, correct result is output:

 author title price 

Explanation

Appropriate use of the Muenchian grouping method.

+2
source

You can use <xsl:for-each select="//book"> to select events

-1
source

All Articles