Sunny
Sunny

Reputation: 47

Compare Dates using XSLT

I'm facing issue with my xslt where I need to output certain value based on comparison of two date fields. XML is as below:

<wd:Worker xmlns:wd="urn:com.workday/bsvc" 
           xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <wd:Worker_Reference wd:Descriptor="will Test">
    <wd:ID wd:type="Employee_ID">11111</wd:ID>
  </wd:Worker_Reference>
  <wd:Worker_Data>
    <wd:Benefit_Enrollment_Data>
      <wd:Health_Care_Data>
        <wd:Health_Care_Period_Data>
          <wd:Health_Care_Coverage_Data>
            <wd:Dependent_Coverage_Data>
              <wd:COBRA_Eligibility_Data>
                <wd:Eligible_Date>2017-10-25-07:00</wd:Eligible_Date>
                <wd:Benefit_Plan_Reference wd:Descriptor="Dental - MetLife">
                  <wd:ID wd:type="Health_Care_Coverage_Plan_ID">Dental-MetLife</wd:ID>
                </wd:Benefit_Plan_Reference>
              </wd:COBRA_Eligibility_Data>
              <wd:COBRA_Eligibility_Data>
                <wd:Eligible_Date>2017-10-25-07:00</wd:Eligible_Date>
                <wd:Benefit_Plan_Reference wd:Descriptor="Vision - VSP">
                  <wd:ID wd:type="Health_Care_Coverage_Plan_ID">Vision -VSP</wd:ID>
                </wd:Benefit_Plan_Reference>
              </wd:COBRA_Eligibility_Data>
            </wd:Dependent_Coverage_Data>
          </wd:Health_Care_Coverage_Data>
          <wd:Health_Care_Coverage_Data>
            <wd:Dependent_Coverage_Data>
              <wd:COBRA_Eligibility_Data>
                <wd:Eligible_Date>2016-10-20-07:00</wd:Eligible_Date>
                <wd:Benefit_Plan_Reference wd:Descriptor="Dental - MetLife">
                  <wd:ID wd:type="Health_Care_Coverage_Plan_ID">Dental-MetLife</wd:ID>
                </wd:Benefit_Plan_Reference>
              </wd:COBRA_Eligibility_Data>
              <wd:COBRA_Eligibility_Data>
                <wd:Eligible_Date>2016-10-20-07:00</wd:Eligible_Date>
                <wd:Benefit_Plan_Reference wd:Descriptor="Vision - VSP">
                  <wd:ID wd:type="Health_Care_Coverage_Plan_ID">Vision -VSP</wd:ID>
                </wd:Benefit_Plan_Reference>
              </wd:COBRA_Eligibility_Data>
            </wd:Dependent_Coverage_Data>
          </wd:Health_Care_Coverage_Data>
        </wd:Health_Care_Period_Data>
      </wd:Health_Care_Data>
    </wd:Benefit_Enrollment_Data>
  </wd:Worker_Data>
</wd:Worker>

I've to write code to select values only from health care coverage data, where wd:Eligibility_Date is greater than today. Below is my xsl which isn't working. Please help:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:wd="urn:com.workday/bsvc"
                xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
                exclude-result-prefixes="xs wd env"
               version="2.0">

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/wd:Worker">    

    <xsl:variable name="today" select="current-date()"/>                 
    <Mydata>          
      <cobra>                           
        <xsl:for-each 
            select="xs:date(/wd:Worker
                            /wd:Worker_Data
                            /wd:Benefit_Enrollment_Data
                            /wd:Health_Care_Data
                            /wd:Health_Care_Period_Data
                            /wd:Health_Care_Coverage_Data
                            /wd:Dependent_Coverage_Data
                            /wd:COBRA_Eligibility_Data
                            /wd:Eligible_Date) 
                    &gt;$today ">                  
          <xsl:value-of select="'TRUE'"/>   
        </xsl:for-each>                 
      </cobra>
    </Mydata> 
  </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Views: 476

Answers (1)

C. M. Sperberg-McQueen
C. M. Sperberg-McQueen

Reputation: 25054

In an xsl:for-each, the select attribute is used to select a sequence of items. Once the sequence of items is selected, the sequence constructor contained by the for-each is evaluated once for each selected item.

You don't describe what results you are expecting, and you don't explain what is wrong about the output you are getting. But from the data and the logic of its probable real-world semantics, it looks as if you want some action to be performed either for every wd:Worker element for whom a health-care eligibility date lies in the future, or for every wd:COBRA_Eligibility_Data with a wd:EligibilityDate child in the future. Or maybe it's some other element you want: you mention wd:HealthCareCoverageData and perhaps that's the one you want.

The first problem that strikes the eye of a reader is that the select attribute on your for-each is not selecting any of those possibly intended sets of nodes. It's not selecting any nodes at all. Instead, the main operator in the select in the XSLT shown is >, which means that in the absence of errors the expression denotes a singleton sequence containing a Boolean value. The Boolean value will be 'true' if there is some wd:EligibilityDate in the document denoting a date in the future; it will be 'false' if there is no such element.

For each item in that sequence (it's a singleton sequence, so there will always be exactly one), the XSLT engine will evaluate the sequence constructor consisting of one xsl:value-of instruction:

<xsl:value-of select="'TRUE'"/>  

I would expect this stylesheet to produce exactly one text node reading 'TRUE' each time it is run. It should be stressed that the expression 'TRUE' is evaluated once for every item in the sequence selected by the select expression, and since the expression 'TRUE' does not depend on the currently selected item in any way, the result will be the same whether the Boolean value of the select expression is true or false.

Or rather, all of what I've just said would be true if you weren't getting error messages complaining that the xs:date constructor expects a single string as argument, and you are passing it four elements, which cannot be coerced automatically into a single string.

If you want to evaluate the body of a for-each once for each wd:Worker with elibility dates in the future, your select will need to look something like this:

select=".[descendant::wd:Eligible_Date[xs:date(.) &gt; $today]]"

Note that the value of this expression (barring errors) will be either the current context item (the wd:Worker element which matched the template) or the empty sequence.

If you want to evaluate the body of the for-each once for each wd:HealthCareCoverateData element containing elibility dates in the future, your select will need to look something like this:

select="descendant::wd:HealthCareCoverageData
          [descendant::wd:Eligible_Date
          [xs:date(.) &gt; $today]]"

Again, note that one can tell by looking at this XPath that barring errors its value, if non-empty, will be a sequence of elements.

On the face of it, your code suggests a possibly serious gap between the way XPath and XSLT work and your mental model of them. I would recommend some tutorials or courses. At the very least, Read some of The Fine Manuals.

Upvotes: 1

Related Questions