我试图在XSLT中应用一种算法,该算法选择从具有租用和期限日期列表的来源提供的特定雇用日期。我需要保留两个文档片段列表,这些片段可能有也可能没有相同数量的节点,尽可能同步(这可能不是最好的方法。)我只想要一个日期返回,并且该日期需要是相应终止日期后91天的最近雇用日期。如果没有找到日期,请返回原始的雇用日期。
从其他帖子中读到,我知道XSLT没有" break" for-each的语句,而递归通常是更好的选择。但我很难想到如何使用递归或模板,甚至如何简洁地只选择我想要的单个节点。
以下是源文档示例:
<?xml version="1.0" encoding="UTF-8"?>
<Report_Data>
<Report_Entry>
<name>Kenneth</name>
<RecentHireDate>2014-12-01-07:00</RecentHireDate>
<OriginalHireDate>2000-01-01-07:00</OriginalHireDate>
<TermDate>2014-10-30-07:00</TermDate>
<Event_History>
<Effective_Date>2000-01-01-07:00</Effective_Date>
<Transaction_Types Descriptor="Hire - Hire Employee Event">
<ID type="Business_Process_Type">Hire Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-01-15-08:00</Effective_Date>
<Transaction_Types Descriptor="Termination - Terminate Employee Event">
<ID type="Business_Process_Type">Terminate Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-02-01-07:00</Effective_Date>
<Transaction_Types Descriptor="Hire - Hire Employee Event">
<ID type="Business_Process_Type">Hire Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-03-01-07:00</Effective_Date>
<Transaction_Types Descriptor="Termination - Terminate Employee Event">
<ID type="Business_Process_Type">Terminate Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-09-30-07:00</Effective_Date>
<Transaction_Types Descriptor="Hire - Hire Employee Event">
<ID type="Business_Process_Type">Hire Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-10-30-07:00</Effective_Date>
<Transaction_Types Descriptor="Termination - Terminate Employee Event">
<ID type="Business_Process_Type">Terminate Employee</ID>
</Transaction_Types>
</Event_History>
<Event_History>
<Effective_Date>2014-12-01-07:00</Effective_Date>
<Transaction_Types Descriptor="Hire - Hire Employee Event">
<ID type="Business_Process_Type">Hire Employee</ID>
</Transaction_Types>
</Event_History>
</Report_Entry>
</Report_Data>
这是XSLT的精简版本,它不能正常工作:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:foo="Foo"
exclude-result-prefixes="xs foo"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Report_Data">
<xsl:for-each select="Report_Entry">
<!-- Gather up all the hire events, sort them descending -->
<xsl:variable name="hireDates">
<xsl:for-each select="Event_History[contains(Transaction_Types, 'Hire')]/Effective_Date">
<xsl:sort select="position()" order="descending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<!-- Gather up all the term events, sort them descending -->
<xsl:variable name="termDates">
<xsl:for-each select="Event_History[contains(Transaction_Types, 'Term')]/Effective_Date">
<xsl:sort select="position()" order="descending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<name><xsl:value-of select="name"/></name>
<statusDate>
<!-- pass in the two document fragment variables, and the previous/original hire date. -->
<xsl:call-template name="foo:getStatusDate">
<xsl:with-param name="hireDates" select="$hireDates"/>
<xsl:with-param name="termDates" select="$termDates"/>
<xsl:with-param name="originalHire" select="OriginalHireDate"/>
</xsl:call-template>
</statusDate>
</xsl:for-each>
</xsl:template>
<xsl:template name="foo:getStatusDate">
<xsl:param name="hireDates"/>
<xsl:param name="termDates"/>
<xsl:param name="originalHire" />
<xsl:variable name="originalHireDate" select="xs:date($originalHire)"/>
<!-- Loop over hireDate document fragment to get all the effective dates -->
<xsl:for-each select="$hireDates/Effective_Date">
<!-- Save a reference to the current record as an actual date. This is so
I can do a "date diff" of sorts later. -->
<xsl:variable name="hireDate" select="." as="xs:date"/>
<xsl:variable name="hirePos">
<xsl:choose>
<xsl:when test="$termDates/Effective_Date/last() >= position()">
<xsl:value-of select="position()"></xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$termDates/Effective_Date/last()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Grab the term date that is in the same position as the hire date.
This is what I'm trying to use to keep them in sync (and failing) -->
<xsl:variable name="termDate" select="$termDates/Effective_Date[$hirePos]" as="xs:date"/>
<!-- Diff the two dates, which will return an integer for the number of days between. -->
<xsl:variable name="dayDiffTermRehire" select="days-from-duration($hireDate - $termDate)" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$dayDiffTermRehire >= xs:integer(91)">
<xsl:sequence select="$hireDate"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$originalHireDate"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我一开始尝试使用某个功能,现在我正在尝试call-template
,但结果基本相同,这是dayDiffTermRehire
变量到期时的错误我认为,与雇用日期相比,选择适当的期限日期和不等数量的期限日期的方法不正确。
编辑:对于此特定输入,正确的雇用日期将是2014-09-30-07:00
,因为将其与相应的终止日期2014-03-01-07:00
进行比较,将是第一个大于91天的日期。
更清晰: 实际上,我需要比较这样的日期。仅适用于每一行。一旦到达上一个学期日期,只需返回原来的雇用日期。
| Hire Dates: | Term Dates: |
| 2000-01-01-07:00 | |
| 2014-02-01-07:00 | 2014-01-15-08:00 |
| 2014-09-30-07:00 | 2014-03-01-07:00 |
| 2014-12-01-07:00 | 2014-10-30-07:00 |
答案 0 :(得分:1)
我试图将您的描述表达为XSLT / XPath:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Report_Data">
<xsl:for-each select="Report_Entry">
<!-- Gather up all the hire events, sort them descending -->
<xsl:variable name="hireDates" select="reverse(Event_History[contains(Transaction_Types, 'Hire')]/Effective_Date/xs:date(.))"/>
<!-- Gather up all the term events, sort them descending -->
<xsl:variable name="termDates" select="reverse(Event_History[contains(Transaction_Types, 'Term')]/Effective_Date/xs:date(.))"/>
<xsl:variable name="count-of-term-dates" select="count($termDates)"/>
<name><xsl:value-of select="name"/></name>
<statusDate>
<xsl:variable name="selectedDates" select="$hireDates[let $pos := index-of($hireDates, .) return (days-from-duration(. - $termDates[if ($pos gt $count-of-term-dates) then $count-of-term-dates else $pos]) >= 91)]"/>
<xsl:value-of select="if (exists($selectedDates[1])) then $selectedDates[1] else xs:date(OriginalHireDate)"/>
</statusDate>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
您的样本的结果是
<name>Kenneth</name>
<statusDate>2014-09-30-07:00</statusDate>
缺点:它是XSLT 3.0,因为它在XPath中使用let
,所以它只能运行像Saxon 9.7或Exselt这样的XSLT 3.0处理器或者oXygen中提供的商业版Saxon 9.6。
如果需要使用XSLT 2.0,则重写用于
的变量表达式 <xsl:variable name="selectedDates" select="for $date in $hireDates, $pos in index-of($hireDates, $date) return $date[days-from-duration(. - $termDates[if ($pos gt $count-of-term-dates) then $count-of-term-dates else $pos]) >= 91]"/>