Search code examples
xmlxsltxslt-1.0xslt-2.0

I need to reorder xml elements through X.SLT. I have Meeting Patters which are Monday through Friday, but they are not in chronological order


I am attempting to rearrange outputted xml Meeting Patterns- which have class meeting times for students Mon-Fri. They are not outputted in any particular order. I am trying to do an XSLT transformation to rearrange the times so they start with Monday and end with Friday.

Here is a relevant slice of the xml I'm looking to transform

<wd:Report_Entry>
       <wd:Course_Section_group>
        <wd:Meeting_Patterns wd:Descriptor="R | 8:00 AM - 10:00 AM">
        </wd:Meeting_Patterns>
        <wd:Meeting_Patterns wd:Descriptor="W | 10:00 AM - 12:00 PM">
        </wd:Meeting_Patterns>
        <wd:Meeting_Patterns wd:Descriptor="R | 9:15 AM - 10:15 AM">
        </wd:Meeting_Patterns>
        <wd:Meeting_Patterns wd:Descriptor="M | 9:00 PM - 10:00 PM">
        </wd:Meeting_Patterns>
        <wd:Meeting_Patterns wd:Descriptor="F | 2:00 PM - 3:00 PM">
        
        </wd:Meeting_Patterns>
        
    </wd:Course_Section_group>
    </wd:Report_Entry>

Here is a portion of the XSLT that I attempted to use to reorder the days into chronological order.

``<xsl:template match="wd:Meeting_Patterns">
    
    <wd:Meeting_Patterns>
        <xsl:apply-templates select="@wd:Descriptor[substring(., 1, 1) = 'M']"/>
        <xsl:apply-templates select="@wd:Descriptor[substring(., 1, 1) = 'T']"/>
        <xsl:apply-templates select="@wd:Descriptor[substring(., 1, 1) = 'W']"/>
        <xsl:apply-templates select="@wd:Descriptor[substring(., 1, 1) = 'R']"/>
        <xsl:apply-templates select="@wd:Descriptor[substring(., 1, 1) = 'F']"/>
    </wd:Meeting_Patterns>

    </xsl:template>

<xsl:template match="wd:Descriptor[substring(., 1, 1) = 'M']">
    <Monday>
        <xsl:value-of select="."/>
    </Monday>
</xsl:template>

<xsl:template match="wd:Descriptor[substring(., 1, 1) = 'T']">
    <Tuesday>
        <xsl:value-of select="."/>
    </Tuesday>
</xsl:template>

<xsl:template match="wd:Descriptor[substring(., 1, 1) = 'W']">
    <Wednesday>
        <xsl:value-of select="."/>
    </Wednesday>
</xsl:template>

<xsl:template match="wd:Descriptor[substring(., 1, 1) = 'R']">
    <Thursday>
        <xsl:value-of select="."/>
    </Thursday>
</xsl:template>

<xsl:template match="wd:Descriptor[substring(., 1, 1) = 'F']">
    <Friday>
        <xsl:value-of select="."/>
    </Friday>
</xsl:template>`

` However, this did not rearrange the days in chronological order. Instead it outputted the data like this:

<?xml version="1.0"?>
<Root xmlns:wd="urn:com.workday.report/Simple_Syllabus_Course_Sec_Temp">
<wd:Meeting_Patterns>R | 8:00 AM - 10:00 AM</wd:Meeting_Patterns>
<wd:Meeting_Patterns>W | 10:00 AM - 12:00 PM</wd:Meeting_Patterns>
<wd:Meeting_Patterns>R | 9:15 AM - 10:15 AM</wd:Meeting_Patterns>
<wd:Meeting_Patterns>M | 9:00 PM - 10:00 PM</wd:Meeting_Patterns>
<wd:Meeting_Patterns>F | 2:00 PM - 3:00 PM</wd:Meeting_Patterns>
</Root>

It outputted the data in the same order. I was hoping that by applying the template with a string that started with an "M", that it would output Monday first, Tuesday second, Wednesday third etc..

Would anyone have any idea as to how to go about solving this issue?

I tried to repurpose the answer on this stack overflow question here: Reordering xml elements using XSLT

but this didn't seem to solve my issue, so I decided to post this question.

thanks


Solution

  • To sort the Meeting_Patterns elements by day-of-the-week, consider the following simplified example:

    XML

    <Course_Section_group>
        <Meeting_Patterns Descriptor="R | 8:00 AM - 10:00 AM"/>
        <Meeting_Patterns Descriptor="W | 10:00 AM - 12:00 PM"/>
        <Meeting_Patterns Descriptor="R | 9:15 AM - 10:15 AM"/>
        <Meeting_Patterns Descriptor="M | 9:00 PM - 10:00 PM"/>
        <Meeting_Patterns Descriptor="F | 2:00 PM - 3:00 PM"/>
    </Course_Section_group>
    

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:template match="Course_Section_group">
        <Root>
            <xsl:for-each select="Meeting_Patterns">
                <!-- sort by day of week -->
                <xsl:sort select="index-of(('M', 'T', 'W', 'R', 'F'), substring(@Descriptor, 1, 1))" data-type="number"/>
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </Root>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <Root>
       <Meeting_Patterns Descriptor="M | 9:00 PM - 10:00 PM"/>
       <Meeting_Patterns Descriptor="W | 10:00 AM - 12:00 PM"/>
       <Meeting_Patterns Descriptor="R | 8:00 AM - 10:00 AM"/>
       <Meeting_Patterns Descriptor="R | 9:15 AM - 10:15 AM"/>
       <Meeting_Patterns Descriptor="F | 2:00 PM - 3:00 PM"/>
    </Root>
    

    Further sorting by the time will be more difficult, given that the time format is 12-hour and inconsistent in length.

    If you want your output to have a label for the day-of-the-week, then you should start by grouping the nodes by day-of-the-week, then sort the groups as shown above.


    P.S. As an alternative to:

    <xsl:sort select="index-of(('M', 'T', 'W', 'R', 'F'), substring(@Descriptor, 1, 1))" data-type="number"/>
    

    you could use:

    <xsl:sort select="translate(substring(@Descriptor, 1, 1), 'MTWRF', '12345')"/>
    

    which is also compatible with XSLT 1.0.