[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] Grouping within a group


Subject: Re: [xsl] Grouping within a group
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Sat, 10 Aug 2013 22:22:52 -0400

At 2013-08-10 21:53 -0400, Rick Quatro wrote:
I have a (basically) flat XML file:
...
With some help from the group, I got a single level of nesting, starting
with each new category. However, I just found out that I need some
sub-grouping. My finished XML needs to look like this:
...
Here is my stylesheet. It gives me what I need, except for the nested
<TeachingPoint> and <SuggestedReading> elements. I am not sure exactly how
to approach this with XSLT 1.0.

I have a complete solution below, but I refactored your extensive use of modes and of recursive apply-templates of siblings as I found them very confusing and really unnecessary. In my code below you'll see I use no modes or pushing siblings, and I act on the data in a top-down fashion rather than a sideways fashion.


I hope you find this helpful. Perhaps you can just extract what I did for teaching points and suggested readings if you really want to keep the original code, but I suggest you compare the maintainability of the two solutions.

. . . . . . . . Ken

t:\ftemp>type rick.xml
<?xml version="1.0" encoding="UTF-8"?>
<Cases>
    <Story>
        <Category>Category: Subcategory</Category>
        <CaseTitle>Title One</CaseTitle>
        <Institution>Institution One</Institution >
        <Author>Authors One</Author>
        <History>History One</History>
        <DifferentialDiagnosis>Sick</DifferentialDiagnosis>
        <DifferentialDiagnosis>Sicker</DifferentialDiagnosis>
        <DifferentialDiagnosis>Sickest</DifferentialDiagnosis>
        <TeachingPoint>Point1</TeachingPoint>
        <TeachingPoint>Point2</TeachingPoint>
        <SuggestedReading>Reading1</SuggestedReading>
        <SuggestedReading>Reading2</SuggestedReading>
        <Category>Category One: Subcategory</Category>
        <CaseTitle>Title Two</CaseTitle>
        <Institution>Title Two</Institution >
        <Author>Author Two</Author>
        <History>History Two</History>
        <DifferentialDiagnosis>Sickly</DifferentialDiagnosis>
        <DifferentialDiagnosis>Sicklier</DifferentialDiagnosis>
        <DifferentialDiagnosis>Sickliest</DifferentialDiagnosis>
        <TeachingPoint>Point1</TeachingPoint>
        <TeachingPoint>Point2</TeachingPoint>
        <SuggestedReading>Reading1</SuggestedReading>
        <SuggestedReading>Reading2</SuggestedReading></Story>
</Cases>
t:\ftemp>call xslt rick.xml rick.xsl
<?xml version="1.0" encoding="utf-8"?>

<data>
   <newRecord>
      <Category>Category</Category>
      <Subcategory>Subcategory</Subcategory>
      <Case>1</Case>
      <CaseTitle>Title One</CaseTitle>
      <Institution>Institution One</Institution>
      <Author>Authors One</Author>
      <History>History One</History>
      <DifferentialDiagnosis1>Sick</DifferentialDiagnosis1>
      <DifferentialDiagnosis2>Sicker</DifferentialDiagnosis2>
      <DifferentialDiagnosis3>Sickest</DifferentialDiagnosis3>
      <TeachingPoints>
         <TeachingPoint>Point1</TeachingPoint>
         <TeachingPoint>Point2</TeachingPoint>
      </TeachingPoints>
      <SuggestedReadings>
         <SuggestedReading>Reading1</SuggestedReading>
         <SuggestedReading>Reading2</SuggestedReading>
      </SuggestedReadings>
   </newRecord>
   <newRecord>
      <Category>Category One</Category>
      <Subcategory>Subcategory</Subcategory>
      <Case>2</Case>
      <CaseTitle>Title Two</CaseTitle>
      <Institution>Title Two</Institution>
      <Author>Author Two</Author>
      <History>History Two</History>
      <DifferentialDiagnosis1>Sickly</DifferentialDiagnosis1>
      <DifferentialDiagnosis2>Sicklier</DifferentialDiagnosis2>
      <DifferentialDiagnosis3>Sickliest</DifferentialDiagnosis3>
      <TeachingPoints>
         <TeachingPoint>Point1</TeachingPoint>
         <TeachingPoint>Point2</TeachingPoint>
      </TeachingPoints>
      <SuggestedReadings>
         <SuggestedReading>Reading1</SuggestedReading>
         <SuggestedReading>Reading2</SuggestedReading>
      </SuggestedReadings>
   </newRecord>
</data>

t:\ftemp>type rick.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes" />

    <xsl:template match="Cases/Story">
        <data>
            <xsl:apply-templates select="Category" />
        </data>
    </xsl:template>

    <xsl:key name="category" match="Story/*[not(self::Category)]"
             use="generate-id(preceding-sibling::Category[1])"/>

     <xsl:template match="Category">
        <newRecord>
            <Category><xsl:value-of select="substring-before(.,':
')"/></Category>
            <Subcategory><xsl:value-of select="substring-after(.,':
')"/></Subcategory>
            <Case><xsl:number count="Category"/></Case>
            <xsl:apply-templates select="key('category',generate-id(.))"/>
        </newRecord>
    </xsl:template>

    <xsl:template match="Story/*" priority="-1">
        <xsl:copy-of select="." />
    </xsl:template>

<xsl:template match="DifferentialDiagnosis">

        <xsl:variable name="diagnosis">
            <xsl:value-of select="1 +
count(preceding-sibling::DifferentialDiagnosis) -
count(preceding-sibling::Category[1]/preceding-sibling::DifferentialDiagnosis)"/>
        </xsl:variable>
        <xsl:element name="{concat(name(),$diagnosis)}"><xsl:value-of
select="."/></xsl:element>
    </xsl:template>

<xsl:key name="teaching" match="TeachingPoint"
         use="generate-id(preceding-sibling::Category[1])"/>

<xsl:template match="TeachingPoint">
  <xsl:if test="not(preceding-sibling::*[1][self::TeachingPoint])">
    <!--now at the first of a set of teaching points-->
    <TeachingPoints>
      <xsl:copy-of select="key('teaching',
                               generate-id(preceding-sibling::Category[1]))"/>
    </TeachingPoints>
  </xsl:if>
</xsl:template>

<xsl:key name="suggested" match="SuggestedReading"
         use="generate-id(preceding-sibling::Category[1])"/>

<xsl:template match="SuggestedReading">
  <xsl:if test="not(preceding-sibling::*[1][self::SuggestedReading])">
    <!--now at the first of a set of suggested readings-->
    <SuggestedReadings>
      <xsl:copy-of select="key('suggested',
                               generate-id(preceding-sibling::Category[1]))"/>
    </SuggestedReadings>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>

t:\ftemp>rem Done!


-- Public XSLT, XSL-FO, and UBL classes in the Netherlands Oct 2013 | Public XSLT, XSL-FO, UBL and code list classes in Australia Oct 2013 | Contact us for world-wide XML consulting and instructor-led training | Free 5-hour lecture: http://www.CraneSoftwrights.com/links/udemy.htm | Crane Softwrights Ltd. http://www.CraneSoftwrights.com/s/ | G. Ken Holman mailto:gkholman@xxxxxxxxxxxxxxxxxxxx | Google+ profile: https://plus.google.com/116832879756988317389/about | Legal business disclaimers: http://www.CraneSoftwrights.com/legal |


Current Thread
Keywords