给出具有日期属性的元素列表,例如
<foo>
<bar date="2001-04-15"/>
<bar date="2002-01-01"/>
<bar date="2005-07-04"/>
<bar date="2010-11-10"/>
</foo>
我想使用XSLT检索最接近但不在给定日期之后的元素。
使用参数“2008-01-01”调用此函数应打印<bar date="2005-07-04">
。假设上下文节点已经是<foo>
。
我不确定什么会更容易,但我也可以设置三个属性:日,月,年,而不是有一个日期属性。
答案 0 :(得分:3)
对于XSLT 1.0,这很棘手,因为它不支持日期作为第一类值,也不支持字符串的字典比较。并且它(不符合规范)不支持在临时变量中构造节点集,然后从该集合中提取单个节点。尽管如此,你想要的只是一些技巧。
您的日期为YYYY-MM-DD这一事实意味着,如果您删除连字符并将结果字符串视为数字,则按数字顺序对这些数字进行排序会产生与按时间顺序排序原始日期相同的结果订购。虽然XSLT没有可更新的变量,但是你可以通过一个模板逐个地将自己递归地应用于兄弟节点,在模板参数中传递状态来获得类似的效果。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="targetDate" select="20080101" />
<xsl:template match="foo">
<!-- find the first "candidate" bar whose date is before the target date -->
<xsl:apply-templates select="(bar[translate(@date, '-', '') < $targetDate])[1]" />
</xsl:template>
<xsl:template match="bar">
<xsl:param name="closest" select="." />
<!-- find the next candidate bar whose date is before the target date -->
<xsl:variable name="nextCandidate"
select="(following-sibling::bar[translate(@date, '-', '') < $targetDate])[1]" />
<xsl:choose>
<xsl:when test="$nextCandidate">
<xsl:choose>
<xsl:when test="translate($nextCandidate/@date, '-', '') > translate($closest/@date, '-', '')">
<!-- $nextCandidate is closer to the target than the current $closest -->
<xsl:apply-templates select="$nextCandidate">
<xsl:with-param name="closest" select="$nextCandidate" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<!-- current $closest is still the closest -->
<xsl:apply-templates select="$nextCandidate">
<xsl:with-param name="closest" select="$closest" />
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- no more candidates, so $closest is the node we require -->
<xsl:copy-of select="$closest" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:2)
这是一个XSLT 2.0选项...
XML输入
<foo>
<bar date="2001-04-15"/>
<bar date="2005-07-04"/>
<bar date="2002-01-01"/>
<bar date="2010-11-10"/>
</foo>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="threshold" select="xs:date('2008-01-01')"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo">
<xsl:variable name="closestDate" as="node()*">
<xsl:apply-templates select="bar[$threshold >= xs:date(@date)]">
<xsl:sort select="@date" data-type="text"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:copy-of select="$closestDate[last()]"/>
</xsl:template>
</xsl:stylesheet>
XML输出
<bar date="2005-07-04"/>
“foo”模板的解释......
<xsl:template match="foo">
<!--First a variable named 'closestDate' is created by doing an
'xsl:apply-templates' to all 'bar' elements that have a '@date'
attribute that is less than or equal to the 'threshold' parameter
(which is '2008-01-01' in the example). Notice that both '@date'
and '$threshold' are cast as 'xs:date' so that the date comparison
will work correctly. Also, we use the 'as="node()*"' attribute to
cast the variable as zero or more nodes() so that each individual
'bar' can be accessed individually.-->
<xsl:variable name="closestDate" as="node()*">
<xsl:apply-templates select="bar[$threshold >= xs:date(@date)]">
<!--This 'xsl:sort' is used to put all the 'bar' elements in order
based on the '@date' attribute.-->
<xsl:sort select="@date" data-type="text"/>
</xsl:apply-templates>
</xsl:variable>
<!--What we end up with for the 'closestDate' variable is this:
<bar date="2001-04-15"/>
<bar date="2002-01-01"/>
<bar date="2005-07-04"/>
In the following 'xsl:copy-of', we choose the last node
in 'closestDate'.-->
<xsl:copy-of select="$closestDate[last()]"/>
</xsl:template>