2010-09-23 35 views
2

我有一個類似如下的XML文件...XSLT:分組和排序....怎麼樣?

<states> 
<state> 
    <name>North Carolina</name> 
    <city>Charlotte</city> 
</state> 
<state> 
    <name>Alaska</name> 
    <city>Fairbanks</city> 
</state> 
<state> 
    <name>Virginia</name> 
    <city>Leesburg</city> 
</state> 
<state> 
    <name>Alaska</name> 
    <city>Coldfoot</city> 
</state> 
<state> 
    <name>North Carolina</name> 
    <city>Harrisburg</city> 
</state> 
<state> 
    <name>Virginia</name> 
    <city>Ashburn</city> 
</state> 
</states> 

我需要產生一個報告,列出每個國家,是按字母順序排列,每個城市以下....等..

Alaska - Fairbanks, Coldfoot 
North Carolina - Charlotte, Harrisburg 
Virginia - Leesburg, Ashburn 

(城市不必爲字母順序,只是狀態)

我試圖這樣做來解決這個一換對每個國家/狀態,按名稱排序,並進行處理。像這樣的....

<xsl:for-each select="states/state"> 
     <xsl:sort select="name" data-type="text" order="ascending"/> 
     <xsl:value-of select="name"/>-<xsl:value-of select="city"/> 
    </xsl:for-each> 

這給了我....

Alaska - Fairbanks 
Alaska - Coldfoot 
North Carolina - Charlotte 
North Carolina - Harrisburg 
Virginia - Leesburg 
Virginia - Ashburn 

的分揀工作,現在我想組。我認爲唯一能做的就是與之前的狀態進行比較,因爲它是排序的,它應該能夠識別狀態值是否沒有改變。像這樣...

<xsl:for-each select="states/state"> 
      <xsl:sort select="name" data-type="text" order="ascending"/> 
    <xsl:variable name="name"><xsl:value-of select="name"> 
    <xsl:variable name="previous-name"><xsl:value-of select="(preceding-sibling::state)/name"> 
    <xsl:if test="$name != $previous-name"> 
    <br/><xsl:value-of select="name"/>- 
    </xsl:if> 
    <xsl:value-of select="city"/> 
</xsl:for-each> 

可悲的是,看來前同輩功能不支持那種很好地工作,所以,通過第一時間(第一阿拉斯加州),它首先看到的北卡羅萊納州的在兄弟姐妹之前。這導致了一些奇怪的結果,這完全不符合我的喜好。

所以,我正在使用XSLT1.0 ...任何想法/建議?

感謝

回答

1

對於XSLT 1.0分組你可能不得不使用Muenchian Method。這可能很難理解,但一旦你得到它的工作,你應該很好去。

+0

開始閱讀有關它....很快就迷路了。將再試一次。 – Doug 2010-09-23 20:24:32

+0

是的,我不會說謊,你已經爲你剪掉了你的作品。我只實施了幾次,但我總是掙扎... – 2010-09-23 20:26:59

4

這個樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="kStateByName" match="state" use="name"/> 
    <xsl:output method="text"/> 
    <xsl:template match="/"> 
     <xsl:apply-templates 
        select="/*/state[count(.|key('kStateByName',name)[1])=1]"> 
      <xsl:sort select="name"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="state"> 
     <xsl:value-of select="concat(name,' - ')"/> 
     <xsl:apply-templates select="key('kStateByName',name)/city"/> 
    </xsl:template> 
    <xsl:template match="city"> 
     <xsl:value-of select="concat(.,substring(', ', 
               1 div (position()!=last())), 
             substring('&#xA;', 
               1 div (position()=last())))"/> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

Alaska - Fairbanks, Coldfoot 
North Carolina - Charlotte, Harrisburg 
Virginia - Leesburg, Ashburn 

注意:由國家的名字分組。分隔符串表達只能與一個拉風(應用模板,市)

的XSLT 2.0解決方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:template match="states"> 
     <xsl:for-each-group select="state" group-by="name"> 
      <xsl:sort select="name"/> 
      <xsl:value-of select="concat(name, 
             ' - ', 
             string-join(current-group()/city,', '), 
             '&#xA;')"/> 
     </xsl:for-each-group> 
    </xsl:template> 
</xsl:stylesheet> 

只是爲了好玩,這個XPath 2.0表達式:

string-join(for $state in distinct-values(/*/*/name) 
      return concat($state, 
          ' - ', 
          string-join(/*/*[name=$state]/city, 
             ', ')), 
      '&#xA;') 
+1

+1,你的男人亞歷杭德羅。 1.0解決方案看起來像Muenchian方法,對嗎? – 2010-09-23 21:09:57

+0

@Abe Miessler:你說得對。這是Muenchian方法。這是XSLT 1.0 – 2010-09-23 21:17:10

+0

+1的標準分組,用於正確和簡短的答案。 – 2010-09-26 12:43:38

0

這將返回一個明確的狀態列表:

<xsl:for-each select="states/state"> 
    <xsl:sort select="name" /> 
    <xsl:if test="not(name = preceding-sibling::state/name)" > 
     <xsl:value-of select="name" /> 
    </xsl:if> 
    </xsl:for-each> 

我用你的例子XML,內置了一個小s tyle片與上述情況,運行它通過的Xalan-J,並返回:

阿拉斯加 北卡羅來納 弗吉尼亞

所以從那裏,你應該能夠應用模板或其他for-each循環拉每個不同州的城市名單。

Chris

+0

當你無法訪問'key'(很久很久以前)時,這就是要走的路。但是如果你將比較作爲複雜度單位,你將會有1 +(N * N)/ 2的複雜度。然後你需要處理這個組。這將是節點集合比較的N'* N複雜度。此外,該測試不是必需的。您可以將該測試作爲謂詞添加到每個選擇表達式中。 – 2010-09-23 23:14:04