4

たとえば、日付属性を持つ要素のリストがあるとします。

<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>

どちらが簡単かはわかりませんが、1 つの日付属性ではなく、日、月、年の 3 つの属性を設定することもできます。

4

2 に答える 2

3

XSLT 1.0 の場合、これは注意が必要です。最初のクラスの値としての日付や、文字列の辞書式比較をサポートしていないからです。また、(仕様ごとに)一時変数でノードセットを構築し、そのセットから個々のノードを抽出することはサポートされていません。それにもかかわらず、あなたが望むことはいくつかのトリックで可能です.

日付が YYYY-MM-DD であるということは、ハイフンを取り除いて結果の文字列を数値として扱う場合、これらの数値を数値順に並べ替えると、元の日付を時系列で並べ替えるのと同じ結果になることを意味します。また、XSLT には更新可能な変数がありませんが、兄弟ノードに 1 つずつ再帰的に適用され、テンプレート パラメーターで状態を渡すテンプレートを使用することで、同様の効果を得ることができます。

<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, '-', '') &lt; $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, '-', '') &lt; $targetDate])[1]" />
  <xsl:choose>
    <xsl:when test="$nextCandidate">
      <xsl:choose>
        <xsl:when test="translate($nextCandidate/@date, '-', '') &gt; 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>
于 2012-12-07T21:27:42.773 に答える
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>
于 2012-12-07T20:25:50.740 に答える