Jump to content

Issue with grouping child nodes


Guest hpatel

Recommended Posts

HiI am very new to xslt. I am trying to group the authors based on their name for each publisher. But I am getting wrong results. Can someone help me what I am doing wrong. Thanks,HemanthSource xml

<?xml version="1.0" encoding="utf-8"?><PublisherList>	<publisher>		<name>Penguin</name>		<books>			<book>				<title>Title One</title>				<author>David</author>			</book>			<book>				<title>Title Two</title>				<author>John</author>			</book>			<book>				<title>Title Three</title>				<author>John</author>			</book>			<book>				<title>Title Four</title>				<author>David</author>			</book>		</books>	</publisher>	<publisher>		<name>Wrox</name>		<books>			<book>				<title>Title Five</title>				<author>David</author>			</book>			<book>				<title>Title Six</title>				<author>John</author>			</book>			<book>				<title>Title Seven</title>				<author>John</author>			</book>			<book>				<title>Title Eight</title>				<author>David</author>			</book>		</books>	</publisher></PublisherList>

Transformation xslt:

<?xml version="1.0" encoding="utf-8"?><xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  <xsl:output method="xml" indent="yes"/>  <xsl:key name="Author" match="publisher/books/book" use="author"/>  <xsl:template match="PublisherList">	<List>	 <xsl:apply-templates select="publisher/books"/>	</List>  </xsl:template>    <xsl:template match="books">	<xsl:for-each select="book[generate-id(.) = generate-id(key('Author', author)[1])]">	  <xsl:sort select="author"/>	  <BookPublisher>		<name>		  <xsl:value-of select="//parent::node()[name()='publisher']/name"/>		</name>		<BooksByAuthor>		  <AuthorName><xsl:value-of select="author"/></AuthorName>		 <xsl:for-each select="key('Author', author)">		  <title>			<xsl:value-of select="title"/>		  </title>		</xsl:for-each>		</BooksByAuthor>	  </BookPublisher>	</xsl:for-each>  </xsl:template> </xsl:transform>

Expected Result:

<?xml version="1.0" encoding="UTF-8"?><List>	<BookPublisher>		<name>Penguin</name>		<BooksByAuthor>			<AuthorName>David</AuthorName>			<title>Title One</title>			<title>Title Four</title>		</BooksByAuthor>		<BooksByAuthor>			<AuthorName>John</AuthorName>			<title>Title Two</title>			<title>Title Three</title>		</BooksByAuthor>	</BookPublisher>	<BookPublisher>		<name>Wrox</name>		<BooksByAuthor>			<AuthorName>David</AuthorName>			<title>Title Five</title>			<title>Title Eight</title>		</BooksByAuthor>		<BooksByAuthor>			<AuthorName>John</AuthorName>			<title>Title Six</title>			<title>Title Seven</title>		</BooksByAuthor>	</BookPublisher></List>

Actual Result from my xslt:

<?xml version="1.0" encoding="UTF-8"?><List>	<BookPublisher>		<name>Penguin</name>		<BooksByAuthor>			<AuthorName>David</AuthorName>			<title>Title One</title>			<title>Title Four</title>			<title>Title Five</title>			<title>Title Eight</title>		</BooksByAuthor>	</BookPublisher>	<BookPublisher>		<name>Penguin</name>		<BooksByAuthor>			<AuthorName>John</AuthorName>			<title>Title Two</title>			<title>Title Three</title>			<title>Title Six</title>			<title>Title Seven</title>		</BooksByAuthor>	</BookPublisher></List>

Link to comment
Share on other sites

  • 2 weeks later...

Damn it... why do the best reported issues have to be the most complicated?XSLT keys are probably the only thing I'm not that familiar with yet and this problem happens to requre it. Drat, drat, and double drat...Well, at least I could point you to this site. They have some interesting articles about XSLT, some of which also discuss keys (and which I'm reading right now, trying to figure keys out btw). Some of them might adress what you need.][edit]This page demonstrates 4 methods of grouping. I think they'll be of most use for you[/edit]

Link to comment
Share on other sites

  • 1 month later...

This one was a toughie. I received (much appreciated) assistance from Michael Kay who steered me in the right direction. The pitfall was the grouping on the sub tree. I quote Mr. Kay

Using Muenchian grouping within a subtree, rather than within the full document, is always a little bit tricky. The usual answer is to define a key that makes the name globally unique, in this case concatenating the publisher name and the author name.Where it's going wrong is here<xsl:for-each select=".//author[count(.| key('authors',.)[1])=1]">where you look for authors within the current publisher, and then test them to see if they are the first author with that name in the document. Try changing the key on author use="concat(ancestor::publisher/name, '#', .)", and changing the above toselect=".//author[count(.| key('authors',concat(current()/name, '#', .)[1])=1]">I don't guarantee that's enough to make it work but it's the essence of the solution.Michael Kay
Taking his advice and with one minor change in his code, I (we) came up with thisThe XML used
<PublisherList>  <publisher>	<name>Penguin</name>	<books>	  <book>		<title>Title One</title>		<author>David</author>	  </book>	  <book>		<title>Title Two</title>		<author>John</author>	  </book>	  <book>		<title>Title Three</title>		<author>John</author>	  </book>	  <book>		<title>Title Four</title>		<author>David</author>	  </book>	</books>  </publisher>  <publisher>	<name>Wrox</name>	<books>	  <book>		<title>Title Five</title>		<author>David</author>	  </book>	  <book>		<title>Title Six</title>		<author>John</author>	  </book>	  <book>		<title>Title Seven</title>		<author>John</author>	  </book>	  <book>		<title>Title Eight</title>		<author>David</author>	  </book>	</books>  </publisher></PublisherList>

The XSLT used

<xsl:stylesheet	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  <xsl:output method="xml"/>  <xsl:key name="authors" match="author" use="concat(ancestor::publisher/name, '#', .)"  />  <xsl:template match="/">	<xsl:element name="List">	  <xsl:for-each select="//publisher">		<xsl:variable name="publisher" select="name"/>		<xsl:element name="BookPublisher">		  <xsl:element name="name">			<xsl:value-of select="$publisher"/>		  </xsl:element>		   <xsl:for-each select=".//author[count(.| key('authors', concat($publisher, '#', .))[1])=1]">			<xsl:element name="BooksByAuthor">			  <xsl:variable name="AuthorName" select="." />			  <xsl:element name="AuthorName">				<xsl:value-of select="$AuthorName"/>			  </xsl:element>			  <xsl:for-each select="//book[ancestor::publisher/name=$publisher][author=$AuthorName]/title">				<xsl:element name="title">				  <xsl:value-of select="."/>				</xsl:element>			  </xsl:for-each>			</xsl:element>		  </xsl:for-each>		</xsl:element>	  </xsl:for-each>	</xsl:element>  </xsl:template></xsl:stylesheet>

the result

<?xml version="1.0"?><List>   <BookPublisher>	  <name>Penguin</name>	  <BooksByAuthor>		 <AuthorName>David</AuthorName>		 <title>Title One</title>		 <title>Title Four</title>	  </BooksByAuthor>	  <BooksByAuthor>		 <AuthorName>John</AuthorName>		 <title>Title Two</title>		 <title>Title Three</title>	  </BooksByAuthor>   </BookPublisher>   <BookPublisher>	  <name>Wrox</name>	  <BooksByAuthor>		 <AuthorName>David</AuthorName>		 <title>Title Five</title>		 <title>Title Eight</title>	  </BooksByAuthor>	  <BooksByAuthor>		 <AuthorName>John</AuthorName>		 <title>Title Six</title>		 <title>Title Seven</title>	  </BooksByAuthor>   </BookPublisher></List>

Looks pretty good to me, Thanks Mike!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...