2009-04-23 99 views
1

我有這樣的XML:XSLT:合併的節點,然後排序

<node id="1"> 
    <data alias="Show">ShowName1</data> 
    <data alias="Dates">21/04/2009,23/04/2009,27/04/2009,</data> 
</node> 

<node id="2"> 
    <data alias="Show">ShowName2</data> 
    <data alias="Dates">22/04/2009,25/04/2009,29/04/2009,</data> 
</node> 

它具有節點X號,每一個節目的名稱,逗號分隔顯示日期的字符串。 我可以標記顯示日期,但我想爲所有節目的所有節目製作一個排序列表,並按日期排序。就像這樣:

<shows> 
    <show> 
     <name>ShowName1</name> 
     <date>21/04/2009</date> 
    </show> 
    <show> 
     <name>ShowName2</name> 
     <date>22/04/2009</date> 
    </show> 
    <show> 
     <name>ShowName1</name> 
     <date>23/04/2009</date> 
    </show> 
    <show> 
     <name>ShowName2</name> 
     <date>25/04/2009</date> 
    </show> 
    <show> 
     <name>ShowName1</name> 
     <date>27/04/2009</date> 
    </show> 
    <show> 
     <name>ShowName2</name> 
     <date>29/04/2009</date> 
    </show> 
</shows> 

這是可能的呢?

+0

XSLT 1.0或2.0? – Tomalak 2009-04-23 14:44:57

回答

2

要做到這一點只用一個變換過程將需要新建的表演元素,被視爲節點集所以模板可以使用內部的xsl:排序力學。 XSL Transformations (XSLT) Version 1.0 spec沒有將自己創建的元素轉換爲節點集的方法。但是,MSXML parser(3.0或更高版本)確實有一個擴展來提供該功能。此外,還有一個exslt extension,我認爲一些更新版本的Firefox支持將允許將此變量動態交換到節點集。但無論如何,這裏有一個可以正確使用MSXML分析器的樣式表。

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
       xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> 
    <xsl:output method="xml"/> 

    <xsl:template match="/"> 
    <xsl:variable name="shows"> 
     <xsl:apply-templates select="//data[@alias='Dates']"/> 
    </xsl:variable> 
    <shows> 
     <xsl:apply-templates select="msxsl:node-set($shows)//show"> 
     <xsl:sort select="substring(date, 7, 4)"/><!-- year --> 
     <xsl:sort select="substring(date, 4, 2)"/><!-- month --> 
     <xsl:sort select="substring(date, 1, 2)"/><!-- day --> 
     </xsl:apply-templates> 
    </shows> 
    </xsl:template> 

    <xsl:template match="show"> 
    <xsl:copy-of select="."/> 
    </xsl:template> 

    <xsl:template match="data[@alias='Dates']"> 
    <xsl:call-template name="eachDate"> 
     <xsl:with-param name="node" select=".."/> 
     <xsl:with-param name="dates" select="."/> 
    </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="eachDate"> 
    <xsl:param name="node" select="."/> 
    <xsl:param name="dates" select="''"/> 
    <xsl:if test="string-length($dates)"> 
     <show> 
     <name><xsl:value-of select="$node/data[@alias='Show']/text()"/></name> 
     <date><xsl:value-of select="substring-before($dates, ',')"/></date> 
     </show> 
     <xsl:call-template name="eachDate"> 
     <xsl:with-param name="node" select="$node"/> 
     <xsl:with-param name="dates" select="substring-after($dates, ',')"/> 
     </xsl:call-template> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

這會使日期排序錯誤。您需要根據單個日期進行排序,*和*它們不是排序友好的「DDMMYYYY」,因此它們也必須進行標記,並在排序前轉爲「YYYYMMDD」。 – Tomalak 2009-04-23 15:36:26

1

由於輸入XML的形狀相當糟糕,我們必須跳過一些環節才能得到您想要的結果。

正如Mister Lucky先生在他的回答中已經概述的那樣,我們必須首先將文檔轉換爲更有用的臨時表格。然後通過擴展函數將更有用的臨時表單轉回到節點集中,並再次處理以產生期望的結果。

在我的答案,我會使用修改後的身份轉變,實現以下臨時形式:

<root> 
    <node id="1"> 
    <data alias="Show">ShowName1</data> 
    <data alias="Dates"> 
     <date sort="20090421">21/04/2009</date> 
     <date sort="20090423">23/04/2009</date> 
     <date sort="20090427">27/04/2009</date> 
    </data> 
    </node> 
    <node id="2"> 
    <data alias="Show">ShowName2</data> 
    <data alias="Dates"> 
     <date sort="20090422">22/04/2009</date> 
     <date sort="20090425">25/04/2009</date> 
     <date sort="20090429">29/04/2009</date> 
    </data> 
    </node> 
</root> 

有了該輸入,排序很簡單,使用<date>元素的@sort屬性。

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
    exclude-result-prefixes="msxsl " 
> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /> 

    <xsl:template match="/"> 
    <!-- prepare our temporary form (a "result tree fragment") --> 
    <xsl:variable name="rtf"> 
     <xsl:apply-templates mode="rtf" /> 
    </xsl:variable> 

    <!-- transform the result tree fragment back to a node-set --> 
    <xsl:variable name="doc" select="msxsl:node-set($rtf)" /> 

    <!-- transform the temporary node-set, sorted by date --> 
    <shows> 
     <xsl:apply-templates select="$doc//date"> 
     <xsl:sort select="@sort" /> 
     </xsl:apply-templates> 
    </shows> 
    </xsl:template> 

    <xsl:template match="date"> 
    <show show_id="{ancestor::node/@id}"> 
     <name><xsl:value-of select="../../data[@alias='Show'][1]/text()" /></name> 
     <date><xsl:value-of select="." /></date> 
    </show> 
    </xsl:template> 

    <!-- all following templates are for producing the temporary form only --> 

    <xsl:template match="@*|node()" mode="rtf"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" mode="rtf" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="data[@alias='Dates']" mode="rtf"> 
    <xsl:copy> 
     <!-- copy all attributes --> 
     <xsl:apply-templates select="@*" mode="rtf" /> 
     <!-- this produces the <date> elements --> 
     <xsl:call-template name="tokenize-datelist" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template name="tokenize-datelist"> 
    <xsl:param name="input" select="." /> 
    <xsl:param name="delim" select="','" /> 

    <xsl:variable name="temp" select="concat($input, $delim)" /> 
    <xsl:variable name="head" select="substring-before($temp, $delim)" /> 
    <xsl:variable name="tail" select="substring-after($input, $delim)" /> 

    <xsl:if test="$head != ''"> 
     <date> 
     <!-- this produces the @sort attribute --> 
     <xsl:call-template name="tokenize-date"> 
      <xsl:with-param name="input" select="$head" /> 
     </xsl:call-template> 
     <xsl:value-of select="$head" /> 
     </date> 
     <xsl:if test="$tail != ''" > 
     <xsl:call-template name="tokenize-datelist"> 
      <xsl:with-param name="input" select="$tail" /> 
      <xsl:with-param name="delim" select="$delim" /> 
     </xsl:call-template> 
     </xsl:if> 
    </xsl:if> 
    </xsl:template> 

    <xsl:template name="tokenize-date"> 
    <xsl:param name="input" select="''" /> 
    <xsl:param name="delim" select="'/'" /> 

    <xsl:variable name="dd" select="substring-before($input, $delim)" /> 
    <xsl:variable name="my" select="substring-after($input, $delim)" /> 
    <xsl:variable name="mm" select="substring-before($my, $delim)" /> 
    <xsl:variable name="yy" select="substring-after($my, $delim)" /> 

    <xsl:attribute name="sort"> 
     <xsl:value-of select="concat($yy, $mm, $dd)" /> 
    </xsl:attribute> 
    </xsl:template> 

</xsl:stylesheet> 

當這是通過運行MSXSL.EXE,XSLT 1.0處理,下面的輸出中產生:

<shows> 
    <show show_id="1"> 
    <name>ShowName1</name> 
    <date>21/04/2009</date> 
    </show> 
    <show show_id="2"> 
    <name>ShowName2</name> 
    <date>22/04/2009</date> 
    </show> 
    <show show_id="1"> 
    <name>ShowName1</name> 
    <date>23/04/2009</date> 
    </show> 
    <show show_id="2"> 
    <name>ShowName2</name> 
    <date>25/04/2009</date> 
    </show> 
    <show show_id="1"> 
    <name>ShowName1</name> 
    <date>27/04/2009</date> 
    </show> 
    <show show_id="2"> 
    <name>ShowName2</name> 
    <date>29/04/2009</date> 
    </show> 
</shows> 
1

I. AN XSLT 1.0溶液使用FXSL 1.x的

這種轉變

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" 
exclude-result-prefixes="ext" 
> 
    <xsl:import href="strSplit-to-Words.xsl"/> 

    <xsl:output indent="yes" omit-xml-declaration="yes"/> 

    <xsl:template match="/*"> 
     <xsl:variable name="vDates"> 
     <xsl:for-each select="node"> 
      <nodeData name="{data[@alias = 'Show']}"> 
       <xsl:call-template name="str-split-to-words"> 
        <xsl:with-param name="pStr" select="data[@alias = 'Dates']"/> 
        <xsl:with-param name="pDelimiters" 
            select="','"/> 
       </xsl:call-template> 
      </nodeData> 
     </xsl:for-each> 
     </xsl:variable> 

     <xsl:apply-templates select="ext:node-set($vDates)/*/*[text()]"> 
     <xsl:sort data-type="number" select="substring(.,7)"/> 
     <xsl:sort data-type="number" select="substring(.,4,2)"/> 
     <xsl:sort data-type="number" select="substring(.,1,2)"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="word"> 
     <show> 
     <name> 
      <xsl:value-of select="../@name"/> 
     </name> 
      <date> 
      <xsl:value-of select="."/> 
      </date> 
     </show> 
    </xsl:template> 
</xsl:stylesheet> 

當在提供的「XML文檔」上應用時,糾正爲格式良好(人們何時會學會提供格式良好的XML文檔?難道困難):

<t> 
    <node id="1"> 
     <data alias="Show">ShowName1</data> 
     <data alias="Dates">21/04/2009,23/04/2009,27/04/2009,</data> 
    </node> 
    <node id="2"> 
     <data alias="Show">ShowName2</data> 
     <data alias="Dates">22/04/2009,25/04/2009,29/04/2009,</data> 
    </node> 
</t> 

產生通緝的結果

<show> 
    <name>ShowName1</name> 
    <date>21/04/2009</date> 
</show> 
<show> 
    <name>ShowName2</name> 
    <date>22/04/2009</date> 
</show> 
<show> 
    <name>ShowName1</name> 
    <date>23/04/2009</date> 
</show> 
<show> 
    <name>ShowName2</name> 
    <date>25/04/2009</date> 
</show> 
<show> 
    <name>ShowName1</name> 
    <date>27/04/2009</date> 
</show> 
<show> 
    <name>ShowName2</name> 
    <date>29/04/2009</date> 
</show> 

II。一個可能的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" 
    exclude-result-prefixes="xs"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <xsl:template match="/*"> 

    <xsl:variable name="vAllData" as="xs:string+"> 
      <xsl:for-each select="node"> 
      <xsl:variable name="vName" select="data[@alias='Show']"/> 

       <xsl:for-each select= 
       "tokenize(data[@alias='Dates'], ',')[.]"> 

       <xsl:value-of select="concat($vName, '+',.)"/> 
      </xsl:for-each> 
      </xsl:for-each> 
    </xsl:variable> 

     <xsl:for-each select="$vAllData"> 
      <xsl:sort data-type="number" select= 
      "substring(substring-after(.,'+'),7)"/> 
      <xsl:sort data-type="number" select= 
      "substring(substring-after(.,'+'),4,2)"/> 
      <xsl:sort data-type="number" select= 
      "substring(substring-after(.,'+'),1,2)"/> 

      <show> 
      <name> 
       <xsl:value-of select="substring-before(.,'+')"/> 
      </name> 
      <date> 
       <xsl:value-of select="substring-after(.,'+')"/> 
      </date> 
      </show> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

當上述XSLT 2.0變換對同一文檔應用中,相同的正確的結果產生