Home > database >  How can I can get a count of all xml items matching complex conditions using xslt and use it in a te
How can I can get a count of all xml items matching complex conditions using xslt and use it in a te

Time:11-12

I need to filter a list of items and only keep items where a date element is more than one day greater than another date element. And then I also need to know how many filtered items are left and terminate the whole thing if there are none. I've simplified my data but it's more or less this:

<data>
    <person>
        <name>Tyler</name>
    </person>
    <items>
        <item>
            <title>A</title>
            <start_date>10/31/2021</start_date>
            <end_date>11/01/2021</end_date>
        </item>
        <item>
            <title>B</title>
            <start_date>08/05/2021</start_date>
            <end_date>08/10/2021</end_date>
        </item>
        <item>
            <title>C</title>
            <start_date>09/04/2021</start_date>
            <end_date>09/05/2021</end_date>
        </item>
    </items>
</data>

So in that example only B would be kept and the message would send. But if B was instead

        <item>
            <title>B</title>
            <start_date>08/05/2021</start_date>
            <end_date>08/06/2021</end_date>
        </item>

The message wouldn't send.

So far I've worked out a way to transform the text dates using the method suggested here. And that works for filtering the list, but I have no idea how to figure out if the resulting list has any elements in it and then how to use that in the terminate statement. Any help would be greatly appreciated and thank you in advance! Here's where I am on the xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/data">

        <xsl:if test="???? Whatever can figure out the number of elements left ????">
            <xsl:message terminate="yes">There are no items left</xsl:message>
        </xsl:if>
        <html>
            <head>
                <title></title>
            </head>
            <body>
                <p>
                    <xsl:text>Name: </xsl:text>
                    <xsl:value-of select="person/name"/>
                </p>
                <table>
                    <thead>
                        <tr><th>Title</th></tr>
                    </thead>

                    <xsl:for-each select="items/item">

                        <xsl:variable name="JDN_start_date">
                            <xsl:call-template name="JDN">
                                <xsl:with-param name="date" select="start_date" />
                            </xsl:call-template>
                        </xsl:variable>

                        <xsl:variable name="JDN_end_date">
                            <xsl:call-template name="JDN">
                                <xsl:with-param name="date" select="end_date" />
                            </xsl:call-template>
                        </xsl:variable>

                        <xsl:if test="($JDN_end_date - $JDN_start_date) &gt; 1">
                            <tr>
                                <td><xsl:value-of select="title"/></td>
                            </tr>
                        </xsl:if>
                    </xsl:for-each>

                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template name="JDN">        <!-- Date string to Julian day number -->
        <xsl:param name="date"/>
        <xsl:param name="year" select="substring($date, 7, 4)"/>
        <xsl:param name="month" select="substring($date, 1, 2)"/>
        <xsl:param name="day" select="substring($date, 4, 2)"/>
        <xsl:param name="a" select="floor((14 - $month) div 12)"/>
        <xsl:param name="y" select="$year   4800 - $a"/>
        <xsl:param name="m" select="$month   12*$a - 3"/>
        <xsl:value-of select="$day   floor((153*$m   2) div 5)   365*$y   floor($y div 4) - floor($y div 100)   floor($y div 400) - 32045" />
    </xsl:template>

</xsl:stylesheet>

CodePudding user response:

Consider the following example:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/data">
    <!-- 1. calculate durations -->
    <xsl:variable name="items">
        <xsl:for-each select="items/item">
            <xsl:copy>
                <xsl:attribute name="duration">
                    <xsl:call-template name="date-difference">
                        <xsl:with-param name="date1" select="start_date" />
                        <xsl:with-param name="date2" select="end_date" />
                    </xsl:call-template>
                </xsl:attribute>
                <xsl:copy-of select="*"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:variable>
    <!-- 2. find items with duration > 1 -->
    <xsl:variable name="pass-items" select="exsl:node-set($items)/item[@duration > 1]" />
    <xsl:if test="not($pass-items)">
        <xsl:message terminate="yes">no items pass</xsl:message>
    </xsl:if>
    <!-- 3. output -->
    <output>
        <xsl:copy-of select="$pass-items"/>
    </output>
</xsl:template> 

<xsl:template name="date-difference">
    <xsl:param name="date1"/>
    <xsl:param name="date2"/>
    <xsl:param name="JDN1">
        <xsl:call-template name="JDN">
            <xsl:with-param name="date" select="$date1" />
        </xsl:call-template>
    </xsl:param>
    <xsl:param name="JDN2">
        <xsl:call-template name="JDN">
            <xsl:with-param name="date" select="$date2" />
        </xsl:call-template>
    </xsl:param>
    <xsl:value-of select="$JDN2 - $JDN1"/>
</xsl:template> 

<xsl:template name="JDN">
    <xsl:param name="date"/>
    <xsl:param name="year" select="substring($date, 7, 4)"/>
    <xsl:param name="month" select="substring($date, 1, 2)"/>
    <xsl:param name="day" select="substring($date, 4, 2)"/>
    <xsl:param name="a" select="floor((14 - $month) div 12)"/>
    <xsl:param name="y" select="$year   4800 - $a"/>
    <xsl:param name="m" select="$month   12*$a - 3"/>
    <xsl:value-of select="$day   floor((153*$m   2) div 5)   365*$y   floor($y div 4) - floor($y div 100)   floor($y div 400) - 32045" />
</xsl:template> 

</xsl:stylesheet>
  • Related