2
votes

I've got this XML file with dates, Sessions and sub-topics. I've got the first two levels working fine, but I can't get the third level to group properly.

Level 1 groups on the Date All sessions on one day should be grouped under that date

Level 2 groups on the Session_Number All Sessions with the same number should be grouped together.

Level 3 should group on the Abstract_Title. If the Abstract_Title is the same, it should appear once with all the Authors listed under it.

Here is my XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml"/>
<xsl:strip-space elements="*"/>

<xsl:key name="sessions-by-startDate" match="session" use="startDate"/>
<xsl:key name="sessions-by-Number" match="session" use="concat(startDate, '|', Session_Number)"/>
<xsl:key name="sessions-by-Abstract" match="stamp" use="concat(startDate, '|', Session_Number, '|', Abstract_Title)"/>
<xsl:template match="sessions">
<Guide>     
<xsl:for-each select="session[generate-id() = generate-id(key('sessions-by-startDate', startDate)[1])]">
<xsl:text>
</xsl:text><SessionDay>
<startDate><xsl:value-of select="startDate"/></startDate>

 <xsl:for-each select="key('sessions-by-startDate', startDate)[generate-id() = generate-id(key('sessions-by-Number', concat(startDate, '|', Session_Number))[1])]">
 <sessions>
 <xsl:apply-templates select="startTime"/>
 <xsl:apply-templates select="Session_Number" />
 <xsl:apply-templates select="Session_Title"/><xsl:text>
 </xsl:text><TopicTitle>Topics &amp; Faculty</TopicTitle>

 <xsl:for-each select="key('sessions-by-Number', concat(startDate, '|', Session_Number))">


 <xsl:for-each
 select="key('sessions-by-Number', concat(startDate, '|', Session_Number))[count(. | key('sessions-by-Abstract', concat(startDate, '|', Session_Number, '|', Abstract_Title))[1]) = 1]">
 <xsl:sort select="Abstract_Title"/>
 <xsl:text>
 </xsl:text><session>  
 <xsl:apply-templates select="Abstract_Title"/>

 <xsl:for-each select="key('sessions-by-Abstract', concat(startDate, '|', Session_Number, '|', Abstract_Title))">
 <xsl:apply-templates select="Author_LastName"/>
 </xsl:for-each>
 </session>  
 </xsl:for-each>


 </xsl:for-each>
 </sessions>

 </xsl:for-each>
 </SessionDay> 
 </xsl:for-each>
 </Guide>    
 </xsl:template>

<xsl:template match="startDate">
<startDate><xsl:value-of select="."/></startDate></xsl:template>

<xsl:template match="Session_Title"><xsl:text>
</xsl:text><Session_Title><xsl:value-of select="."/></Session_Title>    </xsl:template>

<xsl:template match="Session_Number"><xsl:text>
Session Number </xsl:text><Session_Number><xsl:value-of select="."/>    </Session_Number></xsl:template>

<xsl:template match="Abstract_Title"><Abstract_Title><xsl:value-of select="."/>    </Abstract_Title></xsl:template>

<xsl:template match="Author_LastName">
<Author_LastName><xsl:value-of select="."/></Author_LastName></xsl:template>

</xsl:stylesheet>

Here is some sample XML:

<?xml version="1.0" encoding="utf-8"?><sessions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<session><startDate>10/24/2015</startDate><Session_Number>92</Session_Number><Session_Title>Sleep Medicine</Session_Title><Abstract_Title>Concluding Remarks</Abstract_Title><Author_LastName>Stoller</Author_LastName></session>
<session><startDate>10/24/2015</startDate><Session_Number>92</Session_Number><Session_Title>Sleep Medicine</Session_Title><Abstract_Title>Welcome and Introduction</Abstract_Title><Author_LastName>Stoller</Author_LastName></session>
<session><startDate>10/24/2015</startDate><Session_Number>568</Session_Number><Session_Title>Hands-on Simulation</Session_Title><Abstract_Title>Airway</Abstract_Title><Author_LastName>Roth</Author_LastName></session>
<session><startDate>10/24/2015</startDate><Session_Number>568</Session_Number><Session_Title>Hands-on Simulation</Session_Title><Abstract_Title>Airway</Abstract_Title><Author_LastName>Eling</Author_LastName></session>
<session><startDate>10/24/2015</startDate><Session_Number>568</Session_Number><Session_Title>Hands-on Simulation</Session_Title><Abstract_Title>Airway</Abstract_Title><Author_LastName>Bell</Author_LastName></session>
<session><startDate>10/25/2015</startDate><Session_Number>1</Session_Number><Session_Title>Diagnosis of Lung Cancer</Session_Title><Abstract_Title>The Role of EBUS</Abstract_Title><Author_LastName>Silvestri</Author_LastName></session>
<session><startDate>10/25/2015</startDate><Session_Number>1</Session_Number><Session_Title>Diagnosis of Lung Cancer</Session_Title><Abstract_Title>Lung Cancer Staging</Abstract_Title><Author_LastName>Liberman</Author_LastName></session>
<session><startDate>10/25/2015</startDate><Session_Number>1</Session_Number><Session_Title>Diagnosis of Lung Cancer</Session_Title><Abstract_Title>Lung Cancer Staging</Abstract_Title><Author_LastName>Hong</Author_LastName></session>
<session><startDate>10/25/2015</startDate><Session_Number>9</Session_Number><Session_Title>Non-small Cell Lung Cancer??</Session_Title><Abstract_Title>Imaging </Abstract_Title><Author_LastName>Duong</Author_LastName></session>
</sessions>

The resulting XML should look like this:

<?xml version="1.0" encoding="utf-8"?><Guide>
<SessionDay>
<startDate>10/24/2015</startDate>
<sessions>Session Number <Session_Number>92</Session_Number>
<Session_Title>Sleep Medicine</Session_Title>    
<TopicTitle>Topics &amp; Faculty</TopicTitle>

<session><Abstract_Title>Concluding Remarks</Abstract_Title>
<Author_LastName>Stoller</Author_LastName></session>

<session><Abstract_Title>Welcome and Introduction</Abstract_Title>    
<Author_LastName>Stoller</Author_LastName></session>
</sessions>

<sessions>Session Number <Session_Number>568</Session_Number>
<Session_Title>Hands-on Simulation</Session_Title>
<TopicTitle>Topics &amp; Faculty</TopicTitle>

<session><Abstract_Title>Airway</Abstract_Title>     
<Author_LastName>Roth</Author_LastName>
<Author_LastName>Eling</Author_LastName>
<Author_LastName>Bell</Author_LastName></session>
</sessions></SessionDay>

<SessionDay><startDate>10/25/2015</startDate>
<sessions>Session Number <Session_Number>1</Session_Number>
<Session_Title>Diagnosis of Lung Cancer</Session_Title>
<TopicTitle>Topics &amp; Faculty</TopicTitle>

<session><Abstract_Title>The Role of EBUS</Abstract_Title>
<Author_LastName>Silvestri</Author_LastName></session>

<session><Abstract_Title>Lung Cancer Staging</Abstract_Title>
<Author_LastName>Liberman</Author_LastName></session>

<session><Abstract_Title>Lung Cancer Diagnosis</Abstract_Title>
<Author_LastName>Hong</Author_LastName></session></sessions>

<sessions>Session Number <Session_Number>9</Session_Number>
<Session_Title>Non-small Cell Lung Cancer??</Session_Title>
<TopicTitle>Topics &amp; Faculty</TopicTitle>

<session><Abstract_Title>Imaging</Abstract_Title>
<Author_LastName>Duong</Author_LastName></session>
</sessions></SessionDay>
</Guide>

My current XSLT creates the first two groups fine. But I'm getting duplicate Abstract_Title elements. Instead, it should only one unique Abstract_Title elements and list all unique author names thereafter.

I need to add the third key and generate another ID. But I'm not sure where to insert it or how it needs to be written. Any help would be appreciated

1
I updated the XSLT to show my third KEY. But it's not grouping on unique <abstract_title> elements. Can anyone figure out what's wrong with the third Key? - Jim Maivald

1 Answers

1
votes

There were several issues with your XSLT:

1.) xsl:key name="sessions-by-Abstract" was matching stamp instead of session, so the key was not matching anything.

2.) The for-each <xsl:for-each select="key('sessions-by-Number', concat(startDate, '|', Session_Number))"> was selecting the items from the sessions-by-Number key and then using as criteria to select from the same key in the next xsl:for-each, resulting in duplicates.

3.) If you intend to insert a line break in your output, it would be best to use an entity instead of a line break. For example: <xsl:text>&#xA;</xsl:text>. That way, if your XSLT were to be code formatted, you don't run the risk of losing line break or adding additional spaces into your output. However, if you were just trying to better formatting of the output XML, it would be easier to use <xsl:output indent="yes"/>, which will pretty-print (format and indent) the XML.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output method="xml" />
    <xsl:strip-space elements="*"/>

    <xsl:key name="sessions-by-startDate" match="session" use="startDate"/>
    <xsl:key name="sessions-by-Number" match="session" use="concat(startDate, '|', Session_Number)"/>
    <xsl:key name="sessions-by-Abstract" match="session" use="concat(startDate, '|', Session_Number, '|', Abstract_Title)"/>

    <xsl:template match="sessions">
        <Guide>     
            <xsl:for-each select="session[generate-id() = generate-id(key('sessions-by-startDate', startDate)[1])]">
                <xsl:text>&#xA;</xsl:text>
                <SessionDay>
                <startDate><xsl:value-of select="startDate"/></startDate>
                 <xsl:for-each select="key('sessions-by-startDate', startDate)[generate-id() = generate-id(key('sessions-by-Number', concat(startDate, '|', Session_Number))[1])]">
                     <sessions>
                         <xsl:apply-templates select="startTime"/>
                         <xsl:apply-templates select="Session_Number"/>
                         <xsl:apply-templates select="Session_Title"/>
                         <xsl:text>&#xA;</xsl:text>
                         <TopicTitle>Topics &amp; Faculty</TopicTitle>
                         <xsl:for-each
                             select="key('sessions-by-Number', concat(startDate, '|', Session_Number))[count(. | key('sessions-by-Abstract', concat(startDate, '|', Session_Number, '|', Abstract_Title))[1]) = 1]">
                             <xsl:sort select="Abstract_Title"/>
                             <xsl:text>&#xA;</xsl:text>
                             <session>
                                 <xsl:apply-templates select="Abstract_Title"/>

                                 <xsl:for-each
                                     select="key('sessions-by-Abstract', concat(startDate, '|', Session_Number, '|', Abstract_Title))">
                                     <xsl:apply-templates select="Author_LastName"/>
                                 </xsl:for-each>
                             </session>
                         </xsl:for-each>
                     </sessions>
                 </xsl:for-each>
                </SessionDay> 
            </xsl:for-each>
        </Guide>    
    </xsl:template>

    <xsl:template match="startDate">
        <startDate><xsl:value-of select="."/></startDate>
    </xsl:template>

    <xsl:template match="Session_Title">
        <xsl:text>&#xA;</xsl:text>
        <Session_Title><xsl:value-of select="."/></Session_Title>    
    </xsl:template>

    <xsl:template match="Session_Number">
        <xsl:text>Session Number </xsl:text>
        <Session_Number>
            <xsl:value-of select="."/>
        </Session_Number>
    </xsl:template>

    <xsl:template match="Abstract_Title"><Abstract_Title><xsl:value-of select="."/>    
        </Abstract_Title>
    </xsl:template>

    <xsl:template match="Author_LastName">
        <Author_LastName><xsl:value-of select="."/></Author_LastName>
    </xsl:template>

</xsl:stylesheet>