XSLT:当需要其他变量时,如何将来自文档片段 and/or 的 select 节点应用递归到 select 节点?
XSLT: How do I select nodes from a document fragment and/or apply recursion to select nodes when other variables are required?
我正在尝试在 XSLT 中应用一种算法,该算法 select 是从具有雇用日期和任期日期列表的来源提供的特定雇用日期。我需要保持两个文档片段列表,它们可能具有或可能不具有相同数量的节点,尽可能同步(这可能不是最好的方法。)我只想要一个日期 return,并且该日期必须是最近的雇用日期,也就是相应的终止日期之后的 91 天。如果未找到日期,return 原始雇用日期。
阅读其他帖子后,我了解到 XSLT 没有针对每个的 "break" 语句,递归通常是更好的选择。但是我很难考虑如何使用递归或模板,甚至如何简洁地 select 仅考虑我想要从此列表中删除的单个节点。
这是一个示例源文档:
<?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
变量中的一个错误,因为,我认为,select 设置适当的任期日期的方法不正确,并且与雇用日期相比,任期日期的数量不相等。
编辑:对于这个特定的输入,正确的雇用日期应该是 2014-09-30-07:00
,因为将它与相应的终止日期 2014-03-01-07:00
进行比较,将是第一个大于 91 的日期天。
更清晰:
实际上,我需要像这样比较日期。仅针对每一行。一旦到达最后一个学期日期,只需 return 原始雇用日期。
| 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 |
我试图将您的描述表达为 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
,所以它只会 运行 与 XSLT 3.0 处理器如 Saxon 9.7 或 Exselt 或 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]"/>
我正在尝试在 XSLT 中应用一种算法,该算法 select 是从具有雇用日期和任期日期列表的来源提供的特定雇用日期。我需要保持两个文档片段列表,它们可能具有或可能不具有相同数量的节点,尽可能同步(这可能不是最好的方法。)我只想要一个日期 return,并且该日期必须是最近的雇用日期,也就是相应的终止日期之后的 91 天。如果未找到日期,return 原始雇用日期。
阅读其他帖子后,我了解到 XSLT 没有针对每个的 "break" 语句,递归通常是更好的选择。但是我很难考虑如何使用递归或模板,甚至如何简洁地 select 仅考虑我想要从此列表中删除的单个节点。
这是一个示例源文档:
<?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
变量中的一个错误,因为,我认为,select 设置适当的任期日期的方法不正确,并且与雇用日期相比,任期日期的数量不相等。
编辑:对于这个特定的输入,正确的雇用日期应该是 2014-09-30-07:00
,因为将它与相应的终止日期 2014-03-01-07:00
进行比较,将是第一个大于 91 的日期天。
更清晰: 实际上,我需要像这样比较日期。仅针对每一行。一旦到达最后一个学期日期,只需 return 原始雇用日期。
| 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 |
我试图将您的描述表达为 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
,所以它只会 运行 与 XSLT 3.0 处理器如 Saxon 9.7 或 Exselt 或 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]"/>