2012-04-18 22 views
3

我想根據基於子元素的派生值對元素進行排序。無法使用XPath(sum,concat等)計算派生值,但可以使用XSL(xsl:choose,xsl:if等)進行計算。XSLT使用派生/計算值進行排序而不使用EXSLT函數

我會使用EXSLT功能擴展,但它不可用。該環境是XSLT 1.0,Xalan-C++版本1.10,帶有EXSLT公共和集合擴展。

編輯更改了示例以強調我需要組合的派生值不能用簡單的node/xpath函數在xsl:sort語句中計算。

我的目標是列出當前藥物在活動之前,按降序開始日期排序。確定藥物是否是當前藥物的邏輯取決於它是否被取消,尚未過期以及其他一些業務邏輯。

鑑於這種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節點集(連同所需的排序其他值),並使用一鍵生成由-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測試過,結果相同。

我可以通過在$ medication-sorter節點集中包含一個copy-of藥物元素來解決這個問題,但是父上下文會丟失,並且有時候我的樣式表需要它。

是否有另一種方法來對需要使用xsl:template計算的值進行排序/分組?

回答

2

的解決方案可能是很多simplier:

<?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> 
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> 
相關問題