Re: [xsl] Grouping repeating elements within repeating elements

Subject: Re: [xsl] Grouping repeating elements within repeating elements
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Mon, 26 Nov 2007 22:31:12 -0500
At 2007-11-26 13:08 -0700, Cynthia Arangua wrote:
I have been doing XSLT for about four years now and this input XML has me a little stumped as to how to implement. I believe I am using Xalan 2.6 (bundled as part of an EMC Documentum product called WebPublisher) and the only version it supports currently is XSLT 1.0.

This is a particularly difficult problem for XSLT 1.0 because of the organization of the grouping.


Here is the XML input:
...
Basically, I need to group any category/content_category_faq under the appropriate heading designation so like the following:


Benefits
    Health
        Medical Coverage
              Medical Coverage-PPO
              Medical Coverage-HMO
        Dental
     Financial
          Flexible Spending
Life Events
     Getting Married
     Adoption

I am in the introductory stages on development on this so I don't have any existing XSL to demonstrate but I am hoping to make some headway today so I can repost but any initial help would be very cool. I am working on some recurse logic as we speak.

I, too, had to use recursive logic to address this.


A complete solution is below.

As I've often posted, I'm using variables for grouping because of the sub-document context (keys have document-wide context in XSLT 1.0 but can be accessed in a limited fashion in XSLT 2.0).

What was interesting is the organization of your headers ... I made the assumptions that the order is critically important: it isn't just that the headers are the same, but that the same headers are in the same relative position to each other as children of FAQs.

I hope the code below helps ... the output matches your requirement.

. . . . . . . . . . Ken

T:\ftemp>type cindy.xml
<?xml version="1.0" encoding="UTF-8"?>
<content>
<faq_listing>
  <heading>Benefits</heading>
  <heading>Health</heading>
  <heading>Medical Coverage</heading>
  <category>
     <content_category_faq>Medical Coverage - PPO</content_category_faq>
  </category>
  <category>
      <content_category_faq>Medical Coverage - HMO</content_category_faq>
  </category>
</faq_listing>
<faq_listing>
   <heading>Benefits</heading>
   <heading>Health</heading>
   <category>
      <content_category_faq>Dental</content_category_faq>
   </category>
</faq_listing>
<faq_listing>
<heading>Benefits</heading>
<heading>Financial</heading>
  <category>
    <content_category_faq>Flexible Spending</content_category_faq>
  </category>
</faq_listing>
<faq_listing>
   <heading>Life Events</heading>
   <category>
       <content_category_faq>Getting Married</content_category_faq>
   </category>
   <category>
     <content_category_faq>Adoption</content_category_faq>
   </category>
</faq_listing>
</content>

T:\ftemp>type cindy.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="1.0">

<xsl:output method="text"/>

<xsl:template match="/">
  <!--start everything off with the first heading using all FAQs available-->
  <xsl:call-template name="next-heading">
    <xsl:with-param name="faqs" select="/content/faq_listing"/>
  </xsl:call-template>
</xsl:template>

<!--process the next level of heading distinction-->
<xsl:template name="next-heading">
<xsl:param name="index" select="1"/>
<xsl:param name="indent" select="''"/>
<xsl:param name="faqs" select="/.."/>
<!--determine if there are any headings at this level-->
<xsl:if test="$faqs[heading[$index]]">
<!--then there are levels of heading still to separate-->
<xsl:for-each select="$faqs">
<xsl:if test="generate-id(.)=
generate-id($faqs[heading[$index]=
current()/heading[$index]])">
<!--this is a common heading, so display it-->
<xsl:value-of select="concat($indent,heading[$index])"/><xsl:text>
</xsl:text>
<!--determine if there is any information at the next level of depth-->
<xsl:call-template name="next-heading">
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="indent" select="concat($indent,' ')"/>
<xsl:with-param name="faqs" select="$faqs[heading[$index]=
current()/heading[$index]]"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:if>
<!--determine if there are any non-headings at this level-->
<xsl:for-each select="$faqs[heading[$index - 1] and not( heading[$index] )]/
category/content_category_faq">
<!--and display them-->
<xsl:value-of select="concat($indent,.)"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>


</xsl:stylesheet>
T:\ftemp>call xslt cindy.xml cindy.xsl cindy.txt

T:\ftemp>echo on

T:\ftemp>type cindy.txt
Benefits
     Health
          Medical Coverage
               Medical Coverage - PPO
               Medical Coverage - HMO
          Dental
     Financial
          Flexible Spending
Life Events
     Getting Married
     Adoption

T:\ftemp>rem Done!



--
Comprehensive in-depth XSLT2/XSL-FO1.1 classes: Austin TX,Jan-2008
World-wide corporate, govt. & user group XML, XSL and UBL training
RSS feeds:     publicly-available developer resources and training
G. Ken Holman                 mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
Crane Softwrights Ltd.          http://www.CraneSoftwrights.com/s/
Box 266, Kars, Ontario CANADA K0A-2E0    +1(613)489-0999 (F:-0995)
Male Cancer Awareness Nov'07  http://www.CraneSoftwrights.com/s/bc
Legal business disclaimers:  http://www.CraneSoftwrights.com/legal

Current Thread