Ben Esteban
Ben Esteban

Reputation: 59

Filtering and sorting through XSL group

I am trying to filter and organize through a list of people with their attachments. People can have multiple attachments that have been attached or modified at different times or no attachments at all. I only want one attachment per person. If the person has an attachment that contains the word resume I want the latest modified. If the person does not have an attachment with resume in the name which ever attachment was modified last. And if there isn't a resume we still want to include the person but leave those elements blank.

Currently I am trying to sort by FileLastModDate but that's not working.

    <?xml version="1.0" encoding="UTF-8"?>
    <ExportXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <record>
            <field name="FirstName">Olivia</field>
            <field name="LastName">Test</field>
            <field name="Number">1</field>
            <field name="FileName">Olivia Test 2017.pdf</field>
            <field name="FileLastModDate">2017-06-20T19:24:51-04:00</field>
        </record>
        <record>
            <field name="FirstName">Olivia</field>
            <field name="LastName">Tortolini</field>
            <field name="Number">1</field>
            <field name="FileName">Olivia Test 2018.pdf</field>
            <field name="FileLastModDate">2018-06-20T19:24:51-04:00</field>
        </record>
        <record>
            <field name="FirstName">Kevin</field>
            <field name="LastName">X</field>
            <field name="Number">2</field>
            <field name="FileName">cover letter 2018.docx</field>
            <field name="FileLastModDate">2018-10-04T13:32:30-04:00</field>
        </record>
        <record>
            <field name="FirstName">Kevin</field>
            <field name="LastName">X</field>
            <field name="Number">2</field>
            <field name="FileName">Resume 2018.docx</field>
            <field name="FileLastModDate">2018-09-04T13:32:30-04:00</field>
        </record>
        <record>
            <field name="FirstName">Kevin</field>
            <field name="LastName">X</field>
            <field name="Number">2</field>
            <field name="FileName">Resume 2017.docx</field>
            <field name="FileLastModDate">2017-12-26T15:47:54-05:00</field>
        </record>
        <record>
            <field name="FirstName">Michael</field>
            <field name="LastName">S</field>
            <field name="Number">3</field>
            <field name="FileName"></field>
            <field name="FileLastModDate"></field>
        </record>
    </ExportXML>

I am applying a sort to the group, but I'm not getting the right filename

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xpath-default-namespace="http://www.taleo.com/ws/integration/toolkit/2005/07"  xmlns:ns="http://www.taleo.com/ws/integration/toolkit/2005/07"  xmlns:e="http://www.taleo.com/ws/tee800/2009/01" xmlns:fct="http://www.taleo.com/xsl_functions"  exclude-result-prefixes="e fct ns">
    <xsl:output indent="yes"/>
    <xsl:param name="OUTBOUND_FOLDER"/>
    <xsl:param name="NOW"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/ExportXML">
        <Files>
            <xsl:for-each-group select="record" group-by="field[@name=('Number')]">
                <xsl:sort select="field[@name='FileLastModDate']"  order="descending"/>
                <xsl:variable name="FirstName" select="ns:field[@name='FirstName']"/>
                <xsl:variable name="LastName" select="ns:field[@name='LastName']"/>
                <xsl:variable name="LastModifiedDate" select="ns:field[@name='LastModifiedDate']"/>
                <xsl:variable name="Number" select="ns:field[@name='Number']"/>
                <xsl:variable name="FileName" select="field[@name='FileName']"/>
                    <file path="{$FileName}">
                        <FirstName>
                            <xsl:value-of select="$FirstName"/>
                        </FirstName>
                        <LastName>
                            <xsl:value-of select="$LastName"/>
                        </LastName>
                        <LastModifiedDate>
                            <xsl:value-of select="$LastModifiedDate"/>
                        </LastModifiedDate>
                        <Number>
                            <xsl:value-of select="$Number"/>
                        </Number>
                        <FileName>
                            <xsl:value-of select="$FileName"/>
                        </FileName>
                    </file>
            </xsl:for-each-group>      
        </Files>
    </xsl:template>
</xsl:stylesheet>

I should be getting the following:

<?xml version="1.0" encoding="UTF-8"?>
<Files>
   <file path="cover letter 2018.docx">
      <FirstName>Kevin</FirstName>
      <LastName>X</LastName>
      <LastModifiedDate/>
      <Number>2</Number>
      <FileName>Resume 2018.docx</FileName>
   </file>
   <file path="Olivia Test 2017.pdf">
      <FirstName>Olivia</FirstName>
      <LastName>Test</LastName>
      <LastModifiedDate/>
      <Number>1</Number>
      <FileName>Olivia Test 2018.pdf</FileName>
   </file>
   <file path="">
      <FirstName>Michael</FirstName>
      <LastName>S</LastName>
      <LastModifiedDate/>
      <Number>3</Number>
      <FileName/>
   </file>
</Files>

Instead I get the following result

<?xml version="1.0" encoding="UTF-8"?>
<Files>
   <file path="cover letter 2018.docx">
      <FirstName>Kevin</FirstName>
      <LastName>X</LastName>
      <LastModifiedDate/>
      <Number>2</Number>
      <FileName>cover letter 2018.docx</FileName>
   </file>
   <file path="Olivia Test 2017.pdf">
      <FirstName>Olivia</FirstName>
      <LastName>Test</LastName>
      <LastModifiedDate/>
      <Number>1</Number>
      <FileName>Olivia Test 2017.pdf</FileName>
   </file>
   <file path="">
      <FirstName>Michael</FirstName>
      <LastName>S</LastName>
      <LastModifiedDate/>
      <Number>3</Number>
      <FileName/>
   </file>
</Files>

Upvotes: 0

Views: 48

Answers (2)

Tim C
Tim C

Reputation: 70648

Here is a potentially more "elegant" solution, that involves performing a sort on the records first, with the ones with resume coming out first.

Try this XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xpath-default-namespace="http://www.taleo.com/ws/integration/toolkit/2005/07"
  xmlns:ns="http://www.taleo.com/ws/integration/toolkit/2005/07"
  xmlns:e="http://www.taleo.com/ws/tee800/2009/01"
  xmlns:fct="http://www.taleo.com/xsl_functions"
  exclude-result-prefixes="e fct ns">

  <xsl:output indent="yes"/>
  <xsl:param name="OUTBOUND_FOLDER"/>
  <xsl:param name="NOW"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/ExportXML">
    <xsl:variable name="records">
      <xsl:perform-sort select="record">
        <xsl:sort select="matches(field[@name='FileName'], 'Resume', 'i')" order="descending"/>
        <xsl:sort select="field[@name='FileLastModDate']" order="descending"/>
      </xsl:perform-sort>
    </xsl:variable>

    <Files>
      <xsl:for-each-group select="$records/record" group-by="field[@name='Number']">
        <xsl:sort select="field[@name='FileLastModDate']" order="descending"/>

        <xsl:variable name="FirstName" select="field[@name='FirstName']"/>
        <xsl:variable name="LastName" select="field[@name='LastName']"/>
        <xsl:variable name="LastModifiedDate" select="field[@name='FileLastModDate']"/>
        <xsl:variable name="Number" select="field[@name='Number']"/>
        <xsl:variable name="FileName" select="field[@name='FileName']"/>

        <file path="{$FileName}">
          <FirstName>
            <xsl:value-of select="$FirstName"/>
          </FirstName>
          <LastName>
            <xsl:value-of select="$LastName"/>
          </LastName>
          <LastModifiedDate>
            <xsl:value-of select="$LastModifiedDate"/>
          </LastModifiedDate>
          <Number>
            <xsl:value-of select="$Number"/>
          </Number>
          <FileName>
            <xsl:value-of select="$FileName"/>
          </FileName>
        </file>
      </xsl:for-each-group>      
    </Files>
  </xsl:template>
</xsl:stylesheet>

If nothing else, it demonstrates the use of the xsl:perform-sort if you haven't seen that before. And, as a bonus, shows an alternative way to do the case-insensitive check on the name, using matches instead.

Upvotes: 1

zx485
zx485

Reputation: 29042

You can replace your

<xsl:variable name="FileName" select="field[@name='FileName']"/>

variable with a more complex one. The first xsl:when is for the Resumes, the second for the other attachements, and the third for the rest - it could be omitted, but I put it there in case you want some result other than ''.

<xsl:variable name="FileName">
    <xsl:choose>
        <xsl:when test="current-group()/field[@name='FileName'][contains(.,'Resume')]">
            <xsl:for-each select="current-group()/field[@name='FileName']">
                <xsl:sort select="contains(.,'Resume')" order="descending" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="."/>
                </xsl:if>
            </xsl:for-each>
        </xsl:when>
        <xsl:when test="current-group()/field[@name='FileName']">
            <xsl:for-each select="current-group()/field[@name='FileLastModDate']">
                <xsl:sort select="."  order="descending" />
                <xsl:if test="position() = 1">
                    <xsl:value-of select="../field[@name='FileName']"/>
                </xsl:if>
            </xsl:for-each>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="''"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Disclaimer: There may be other, more elegant, solutions.


To make the 'Resume' comparisons case-insensitive, you can use the upper-case(...) function. and then replace all the contains(.,'Resume') with

contains(upper-case(.),'RESUME')

Upvotes: 1

Related Questions