Re: [xsl] Grouping repeating elements within repeating elements

Subject: Re: [xsl] Grouping repeating elements within repeating elements
From: Cynthia.Arangua@xxxxxxxxxxxxxx
Date: Wed, 5 Dec 2007 12:42:46 -0600
Strike what I had asked for before.  I think I got that working.  But now,
I am running into another issue.

This code is basically building out an FAQ page.  But my problem really is
is that there are cases where there will be no headings (i.e. a parent
node).  If that is the case, I just need to display the questions and
answers and bypass wrapping in a div tag that the values from the headings
would produce.

I have it working now where if there are no headings, then just display the
questions and answers.  But unfourtunately, the xsl:if that I have in place
is getting stepped into even by questions and answers that do have headings
and they are showing up as duplicates.

The XML input below is still the same but the only variance is that I pull
in the content_category_faq value and pass that value into a query to pull
back all the XML objects that have value and thus display the questions and
answers.

The code is attached below and I have it commented where a more effective
if logic is needed.

I just can't seem to figure out how to get around the duplicate entries in
the questions and answers where there are indeed headings.

Thank you so much for any suggestions.

-----------------------------------------------------------------

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:functx="http://www.functx.com";
 xmlns:xs="http://www.w3.org/2001/XMLSchema";
 xmlns:java="http://xml.apache.org/xslt/java";
 xmlns:xalan="http://xml.apache.org/xalan"; version="1.0"
 exclude-result-prefixes="java xalan">
 <xsl:output method="html"/>
 <!--######################################################################################-->

 <!--Parameters for XDQL processing-->
 <!--######################################################################################-->

 <xsl:param name="DMS_SESSION_ID" select="'default value'"/>
 <xsl:param name="DMS_INPUT_OBJECT_ID"/>
 <!--######################################################################################-->

 <!--Need to query the get the content_category values in the FAQ Listing
 Object to build the Listings-->
 <!--######################################################################################-->

 <xsl:param name="faq" select="content/faq_listing"/>
 <xsl:param name="listing_content_category"
 select="content/faq_listing/category/content_category_faq"/>
 <xsl:param name="heading" select="content/faq_listing/heading"/>
 <!--######################################################################################-->

 <!--MAIN TEMPLATE CALL-->
 <!--######################################################################################-->

 <xsl:template match="/">
  <html>
   <head>
    <style type="text/css">
      .linkMore
      {
       font: bold 10px Tahoma;
       background: url("bulletMore.gif") no-repeat left;
       vertical-align: baseline;
       padding: 0 7px 0 0;
       margin: 0;
      }
      .content_hiearchy_display
      {
       display:block;
      }
      .content_hiearchy_hidden
      {
       display:none;
      )
    </style>
    <script type="text/javascript">
    function ContentHiearchy_FAQ (div_name){
     alert(div_name);
     var divToShow=document.getElementById(div_name);
     if (divToShow.style.display=='block'){
      divToShow.style.display='none';
      }
     else{
      divToShow.style.display='block';
       }
    }
    </script>
   </head>
   <body>
    <xsl:call-template name="next-heading">
     <xsl:with-param name="faqs" select="/content/faq_listing"/>
    </xsl:call-template>
   </body>
  </html>
  <!--start everything off with the first heading using all FAQs
  available-->
 </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:variable name="generatingOuterId" select="generate-id(.)"/>
    <xsl:if test="generate-id(.)=
                    generate-id($faqs[heading[$index]=
                                      current()/heading[$index]])">
     <!--this is a common heading, so display it-->
     <xsl:variable name="generatingHeaderId"
     select="concat(heading[$index],$generatingOuterId)"/>
     <xsl:if test="string(heading[$index])">
      <xsl:text disable-output-escaping="yes"><![CDATA[<a class='linkMore'
      href='#' onclick="ContentHiearchy_FAQ(']]></xsl:text>
      <xsl:value-of select="$generatingHeaderId"/>
      <xsl:text disable-output-escaping="yes"><![CDATA[')">]]></xsl:text>
      <xsl:value-of select="concat($indent,heading[$index])"/>
      <xsl:text disable-output-escaping="yes"><![CDATA[</a><br/> <DIV
      id="]]></xsl:text>
      <xsl:value-of select="$generatingHeaderId"/>
      <xsl:text disable-output-escaping="yes"><![CDATA["
      class="content_hiearchy_hidden">]]></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,'&#160;&#160;&#160;')"/>
       <xsl:with-param name="faqs" select="$faqs[heading[$index]=
                                                  current()/heading[$index]]"/>

      </xsl:call-template>
      <xsl:text disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
     </xsl:if>
    </xsl:if>
    <!--*********************************************-->
    <!--THERE IS NO HEADER-->
    <!--*********************************************-->
    <!--NEED A MORE EFFECTIVE MECHANISM TO CHECK FOR CATEGORIES WITH NO
    HEADINGS-->
    <!--THIS IF STATEMENT IS SUFFICIENT FOR PULLING IN QUESTIONS AND
    ANSWERS WITH NO HEADING BUT PUTS IN COPIES OF QUESTIONS AND ANSWERS IF
    THERE IS A HEADING-->
    <xsl:if test="not(string(heading[$index]))">
    <xsl:value-of select="$index"></xsl:value-of>
    WE ARE IN THE NO HEADER CODE
     <xsl:for-each select="category/content_category_faq">
      <xsl:variable name="generatingCategoryIdOuter"
      select="generate-id(.)"/>
      <xsl:variable name="content_cat" select="."/>
      <xsl:variable name="gettingObjAttrs">select r_object_id from
      sargento_portal_document where any content_category= '<xsl:value-of
      select="$content_cat"/>' and r_object_id!='<xsl:value-of
      select="$DMS_INPUT_OBJECT_ID"/>' and content_type='FAQ' and
      r_object_id not in (select parent_id from dm_relation where child_id
      in (select r_object_id from sargento_portal_document where
      object_name='FAQ_Listing_Template.xml')) </xsl:variable>
      <xsl:variable name="gettingObjAttrs_results">
       <xsl:call-template name="XDQL">
        <xsl:with-param name="dql" select="string($gettingObjAttrs)"/>
        <xsl:with-param name="includecontent" select="string('true')"/>
       </xsl:call-template>
      </xsl:variable>
      <xsl:if
      test="string(xalan:nodeset($gettingObjAttrs_results)/xdql/object/r_object_id)">
       <xsl:for-each
        select="xalan:nodeset($gettingObjAttrs_results)/xdql/object/r_object_id">

        <xsl:variable name="idOfXML" select="."/>
        <xsl:variable name="generatingCategoryId" select="generate-id(.)"/>
        <xsl:variable name="idOfPair"
        select="concat($generatingCategoryIdOuter,(concat($generatingCategoryId,$idOfXML)))"/>

        <xsl:text disable-output-escaping="yes"><![CDATA[<a
        class='linkMore' href='#'
        onclick="ContentHiearchy_FAQ(']]></xsl:text>
        <xsl:value-of select="$idOfPair"/>
        <xsl:text disable-output-escaping="yes"><![CDATA[')">]]></xsl:text>
        <xsl:value-of
        select="concat($indent,../content/content/faq/question)"/>
        <xsl:text disable-output-escaping="yes"><![CDATA[</a>]]></xsl:text>
        <br/>
        <xsl:text disable-output-escaping="yes"><![CDATA[<div
        class="content_hiearchy_hidden linkMore " id="]]></xsl:text>
        <xsl:value-of select="$idOfPair"/>
        <xsl:text disable-output-escaping="yes"><![CDATA[">]]></xsl:text>
        <xsl:value-of
        select="concat($indent,../content/content/faq/answer)"/>
        <xsl:if test="not(string(../content/content/faq_document))">
         <xsl:text
        disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
        </xsl:if>
        <xsl:if test="string(../content/content/faq_document)">
         <xsl:for-each select="../content/content/faq_document">
          <xsl:variable name="getSelectedObject"
          select="document/processing-instruction('dctmEditor')"/>
          <xsl:variable name="objectId"
          select="substring-after($getSelectedObject,'selectedObject=')"/>
          <xsl:variable name="obj" select="substring($objectId,1,16)"/>
          <xsl:variable name="gettingFAQDocAttrs">select r_object_id, title
          from sargento_portal_document where r_object_id='<xsl:value-of
          select="$obj"/>'</xsl:variable>
          <xsl:variable name="gettingFAQDocAttrs_results">
           <xsl:call-template name="XDQL">
            <xsl:with-param name="dql"
            select="string($gettingFAQDocAttrs)"/>
            <xsl:with-param name="includecontent"
            select="string('false')"/>
           </xsl:call-template>
          </xsl:variable>
          <xsl:variable name="document_title"
          select="xalan:nodeset($gettingFAQDocAttrs_results)/xdql/object/title"/>

          <xsl:if test="string(document)">
           <br/>
           <xsl:value-of select="$indent"/>
           <a>
            <xsl:attribute name="href"><xsl:value-of
            select="document"/></xsl:attribute>
            <xsl:attribute name="target">_blank</xsl:attribute>
            <xsl:value-of select="$document_title"/>
            <br/>
           </a>
          </xsl:if>
         </xsl:for-each>
        </xsl:if>
        <xsl:text
        disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
       </xsl:for-each>
      </xsl:if>
     </xsl:for-each>
    </xsl:if>
    <!--*********************************************-->
    <!--END OF CHECKING FOR NO HEADER-->
    <!--*********************************************-->
   </xsl:for-each>
  </xsl:if>
  <xsl:for-each select="$faqs[heading[$index - 1] and not( heading[$index]
  )]/
                        category/content_category_faq">
   <xsl:variable name="generatingCategoryIdOuter" select="generate-id(.)"/>
   <xsl:variable name="content_cat" select="."/>
   <xsl:variable name="gettingObjAttrs">select r_object_id from
   sargento_portal_document where any content_category= '<xsl:value-of
   select="$content_cat"/>' and r_object_id!='<xsl:value-of
   select="$DMS_INPUT_OBJECT_ID"/>' and content_type='FAQ' and r_object_id
   not in (select parent_id from dm_relation where child_id in (select
   r_object_id from sargento_portal_document where
   object_name='FAQ_Listing_Template.xml')) </xsl:variable>
   <xsl:variable name="gettingObjAttrs_results">
    <xsl:call-template name="XDQL">
     <xsl:with-param name="dql" select="string($gettingObjAttrs)"/>
     <xsl:with-param name="includecontent" select="string('true')"/>
    </xsl:call-template>
   </xsl:variable>
   <xsl:if
   test="string(xalan:nodeset($gettingObjAttrs_results)/xdql/object/r_object_id)">
    <xsl:for-each
     select="xalan:nodeset($gettingObjAttrs_results)/xdql/object/r_object_id">

     <xsl:variable name="idOfXML" select="."/>
     <xsl:variable name="generatingCategoryId" select="generate-id(.)"/>
     <xsl:variable name="idOfPair"
     select="concat($generatingCategoryIdOuter,(concat($generatingCategoryId,$idOfXML)))"/>

     <xsl:text disable-output-escaping="yes"><![CDATA[<a class='linkMore'
     href='#' onclick="ContentHiearchy_FAQ(']]></xsl:text>
     <xsl:value-of select="$idOfPair"/>
     <xsl:text disable-output-escaping="yes"><![CDATA[')">]]></xsl:text>
     <xsl:value-of
     select="concat($indent,../content/content/faq/question)"/>
     <xsl:text disable-output-escaping="yes"><![CDATA[</a>]]></xsl:text>
     <br/>
     <xsl:text disable-output-escaping="yes"><![CDATA[<div
     class="content_hiearchy_hidden linkMore " id="]]></xsl:text>
     <xsl:value-of select="$idOfPair"/>
     <xsl:text disable-output-escaping="yes"><![CDATA[">]]></xsl:text>
     <xsl:value-of select="concat($indent,../content/content/faq/answer)"/>
     <xsl:if test="not(string(../content/content/faq_document))">
      <xsl:text disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
     </xsl:if>
     <xsl:if test="string(../content/content/faq_document)">
      <xsl:for-each select="../content/content/faq_document">
       <xsl:variable name="getSelectedObject"
       select="document/processing-instruction('dctmEditor')"/>
       <xsl:variable name="objectId"
       select="substring-after($getSelectedObject,'selectedObject=')"/>
       <xsl:variable name="obj" select="substring($objectId,1,16)"/>
       <xsl:variable name="gettingFAQDocAttrs">select r_object_id, title
       from sargento_portal_document where r_object_id='<xsl:value-of
       select="$obj"/>'</xsl:variable>
       <xsl:variable name="gettingFAQDocAttrs_results">
        <xsl:call-template name="XDQL">
         <xsl:with-param name="dql" select="string($gettingFAQDocAttrs)"/>
         <xsl:with-param name="includecontent" select="string('false')"/>
        </xsl:call-template>
       </xsl:variable>
       <xsl:variable name="document_title"
       select="xalan:nodeset($gettingFAQDocAttrs_results)/xdql/object/title"/>

       <xsl:if test="string(document)">
        <br/>
        <xsl:value-of select="$indent"/>
        <a>
         <xsl:attribute name="href"><xsl:value-of
         select="document"/></xsl:attribute>
         <xsl:attribute name="target">_blank</xsl:attribute>
         <xsl:value-of select="$document_title"/>
         <br/>
        </a>
       </xsl:if>
      </xsl:for-each>
     </xsl:if>
     <xsl:text disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
    </xsl:for-each>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>
 <xsl:template name="XDQL">
  <xsl:param name="dql"/>
  <xsl:param name="includecontent" select="string('false')"/>
  <xsl:variable name="pdql" select="string($dql)"/>
  <xsl:variable name="xdql"
  select="java:com.documentum.xml.xdql.DfXmlQuery.new()"/>
  <xsl:variable name="init" select="java:init($xdql)"/>
  <xsl:variable name="setDql" select="java:setDql($xdql,$pdql)"/>
  <xsl:variable name="setContentEncoding"
  select="java:setContentEncoding($xdql,'dom')"/>
  <xsl:variable name="setContentFormat"
  select="java:setContentFormat($xdql,'xml')"/>
  <xsl:variable name="setRootNode"
  select="java:setRootNode($xdql,'xdql')"/>
  <xsl:variable name="Repeatingasnested"
  select="java:setRepeatingAsNested($xdql,false())"/>
  <xsl:choose>
   <xsl:when test="$includecontent='false'">
    <xsl:variable name="param_includeContent"
    select="java:includeContent($xdql,false())"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="param_includeContent"
   select="java:includeContent($xdql,true())"/>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:variable name="execute"
  select="java:execute($xdql,'DF_READ_QUERY',$DMS_SESSION_ID)"/>
  <xsl:variable name="results" select="java:getXMLDOM($xdql)"/>
  <xsl:copy-of select="$results"/>
 </xsl:template>
</xsl:stylesheet>






-----Cynthia.Arangua@xxxxxxxxxxxxxx wrote: -----


To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
From: Cynthia.Arangua@xxxxxxxxxxxxxx
Date: 12/04/2007 11:03AM
Subject: Re: [xsl] Grouping repeating elements within repeating elements


This solution worked wonderfully but I have two more questions.  How would
you generate a unique id for each layer in the output?  I tried to do
generate-id(.) but in some cases, it generated the same id so I have tried
to add in documentum specific ids that it pulls from the database but not
sure I feel completely comfortable with that approach.  Also, how would we
produce the /category/content_category_faq if there we NO headings.  I am
trying to come up with a solution for that one right now as well.  Thanks!
-----"G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx> wrote: -----


To: <xsl-list@xxxxxxxxxxxxxxxxxxxxxx>
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: 11/26/2007 08:31PM
Subject: Re: [xsl] Grouping repeating elements within repeating elements

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