3

子要素に基づく派生値に基づいて要素を並べ替えたいと思います。sum派生値は、XPath ( 、など)を使用して計算することはできませんがconcat、XSL ( xsl:choose、など) では計算できxsl:ifます。

EXSLT 関数拡張を使用したいのですが、利用できません。この環境は、XSLT 1.0、Xalan-C++ バージョン 1.10 であり、EXSLT 共通および拡張機能が設定されています。

編集xsl:sort例を変更して、グループ化する必要がある派生値は、ステートメント内の単純なノード/xpath 関数では計算できないことを強調しました。

私の目標は、開始日の降順に並べ替えて、現在の薬を非アクティブな薬の前にリストすることです。薬が最新であるかどうかを判断するロジックは、薬がキャンセルされたか、有効期限が切れていないか、およびその他のビジネス ロジックによって異なります。

この XML を考えると:

<?xml version="1.0"?>
<medications>
  <medication>
    <name>med1</name>
    <status>canceled</status>
    <startTime>2012-02-01T00:00:00Z</startTime>
    <endTime>2012-12-31T00:00:00Z</endTime>
    <!-- other elements omitted -->
  </medication>
  <medication>
    <name>med2</name>
    <status />
    <startTime>2012-01-01T00:00:00Z</startTime>
    <endTime>2012-01-07T00:00:00Z</endTime>
    <!-- other elements omitted -->
  </medication>
  <medication>
    <name>med3</name>
    <status />
    <startTime>2012-01-01T00:00:00Z</startTime>
    <!-- other element omitted -->
  </medication>
</medications>

スタイルシートは、例のデータから省略された情報 (注文医師、薬局の場所など) と親ノードからのデータ (患者の住所、主治医など) を含む、並べ替えられた医薬品のリストを生成します。この例では、投薬ノードをトラバースできることを示す単純なソート済みリストを作成します。

<?xml version="1.0" encoding="utf-8"?>
<medications>
  <medication isCurrent="1" name="med3" />
  <medication isCurrent="0" name="med1" />
  <medication isCurrent="0" name="med2" />
</medications>

私が思いついた最善の方法は、導出された値を EXSLT ノード セットに事前に計算し (並べ替えに必要な他の値と共に)、キーを使用して、generate-id によって薬物要素を検索することです。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common"
  exclude-result-prefixes="exsl">
  <xsl:output encoding="utf-8" method="xml" indent="yes" />

  <xsl:key name="medications-by-id" match="medication" use="generate-id()" />
  <xsl:variable name="medication-sorter">
    <xsl:for-each select="//medication">
      <item id="{generate-id(.)}">
        <xsl:attribute name="isCurrent">
          <xsl:apply-templates mode="isCurrentMedication" select="." />
        </xsl:attribute>
        <xsl:attribute name="startTime">
          <xsl:value-of select="startTime/text()" />
        </xsl:attribute>
      </item>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="medications">
    <!-- hardcoded key lookup works -->
    <hardcoded><xsl:value-of select="key('medications-by-id',generate-id(medication[2]))/name/text()"/></hardcoded>

    <!-- but key lookup from the sort helper does not -->
    <medications>
      <xsl:for-each select="exsl:node-set($medication-sorter)/item">
        <xsl:sort select="@isCurrent" order="descending" />
        <xsl:sort select="@startTime" order="descending" />
        <medication>
          <xsl:attribute name="isCurrent">
            <xsl:value-of select="@isCurrent" />
          </xsl:attribute>
          <xsl:attribute name="name">
            <xsl:value-of select="key('medications-by-id',@id)/name/text()" />
          </xsl:attribute>
        </medication>
      </xsl:for-each>
    </medications>
  </xsl:template>

  <xsl:template mode="isCurrentMedication" match="medication">
    <xsl:choose>
      <xsl:when test="(status/text()='canceled') or (status/text()='discontinued') or (status/text()='inactive')">0</xsl:when>
      <xsl:otherwise>
        <!-- omitted date checking logic not relevent to this question, so just hardcoded -->
        <xsl:choose>
          <xsl:when test="name/text()='med2'">0</xsl:when>
          <xsl:when test="name/text()='med3'">1</xsl:when>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

ただし、これは期待どおりに機能しませんでした。generate-id(medication[2]) でキーを検索すると、ノードは有効で名前が出力されますが、値がまったく同じように見えても、ノード セットから @id を使用して呼び出された場合は機能しません。

<?xml version="1.0" encoding="utf-8"?>
<medications>
  <hardcoded>med2</hardcoded>
  <medication isCurrent="1" name="" />
  <medication isCurrent="0" name="" />
  <medication isCurrent="0" name="" />
</medications>

これも Xalan for Java 2.7.1 でテストし、同じ結果を得ました。

copy-of$medication-sorter ノードセットに the drug 要素を含めることでこれを回避できますが、親コンテキストが失われ、私のスタイルシートがそれを必要とする場合があります。

を使用して計算する必要がある値の並べ替え/グループ化にアプローチする別の方法はありxsl:templateますか?

4

2 に答える 2

2

解決策ははるかに単純かもしれません:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="xml" indent="yes"/>
<xsl:template match="books">
    <xsl:copy>
        <xsl:for-each select="book">
            <xsl:sort select="status" order="descending"/>
            <xsl:sort select="number(count) &gt; 0" order="descending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>
于 2012-04-18T06:33:19.933 に答える
0

これはすべて非常に複雑に見えます。重複をグループ化または無視せずに並べ替えるだけの場合は、キーは必要ありません。
次のことを試してください。ステータスとカウントが両方とも降順で並べ替えられ、公開後に絶版が発生し、各ステータス内で最後にゼロカウントが発生します。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/">
        <output>
            <xsl:for-each select="books/book">
                <xsl:sort select="status" order="descending"/>
                <xsl:sort select="count" order="descending"/>
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </output>
    </xsl:template>

</xsl:stylesheet>

結果は、指定したものとは少し異なります。このように満足しているか、少し複雑にする必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <book>
        <title>Three</title>
        <price>30</price>
        <count>3</count>
        <status>Published</status>
    </book>
    <book>
        <title>One</title>
        <price>10</price>
        <count>1</count>
        <status>Published</status>
    </book>
    <book>
        <title>Zero</title>
        <price>5</price>
        <count>0</count>
        <status>Published</status>
    </book>
    <book>
        <title>Two</title>
        <price/>
        <count>0</count>
        <status>Out of Print</status>
    </book>
</output>
于 2012-04-18T06:43:28.833 に答える