2011-04-06 45 views
3

我已經將XML文檔構造幾個XML元素迭代如下XSLT 1.0到超過與逗號分隔的值

<items> 
<item> 
    <name>item1</name> 
    <attributes>a,b,c,d</attributes> 
</item> 
<item> 
    <name>item2</name> 
    <attributes>c,d,e</attributes> 
</item> 
</items> 

對於每個唯一的屬性值(以逗號分隔)我需要列出與該相關聯的所有項名稱值如下所示:

a : item1 
b : item1 
c : item1, item2 
d : item1, item2 
e : item2 

我的最初的計劃是使用模板來解析屬性入屬性節點,圍繞每個用適當的標記,然後用XPATH表達式等

分離出唯一值
Attribute[not(.=following::Attribute)] 

但由於模板的結果不是經歷XML解析器的節點集,我無法遍歷它。我也嘗試過exslt的node-set()函數,僅僅意識到它不允許我遍歷各個Attribute節點。

在這一點上,我很難找到一個簡單的方法來做到這一點,真的很感激任何幫助或想法如何進行。謝謝!

+0

好問題,+1。查看我的答案以獲得完整的解決方案和解釋。 – 2011-04-06 02:43:51

+0

我喜歡思考這個問題 – 2011-04-06 03:06:27

回答

1

這種轉變

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

<xsl:key name="kAtrByVal" match="attr" use="."/> 

<xsl:template match="/"> 
    <xsl:variable name="vrtfPass1"> 
    <groups> 
    <xsl:apply-templates/> 
    </groups> 
    </xsl:variable> 

    <xsl:variable name="vPass1" 
     select="ext:node-set($vrtfPass1)"/> 

    <xsl:apply-templates select="$vPass1/*"/> 
</xsl:template> 

<xsl:template match="item"> 
    <group name="{name}"> 
    <xsl:apply-templates select="attributes"/> 
    </group> 
</xsl:template> 

<xsl:template match="attributes" name="tokenize"> 
    <xsl:param name="pText" select="."/> 

    <xsl:if test="string-length($pText)"> 
    <xsl:variable name="vText" select= 
     "concat($pText,',')"/> 
    <attr> 
    <xsl:value-of select="substring-before($vText,',')"/> 
    </attr> 
    <xsl:call-template name="tokenize"> 
    <xsl:with-param name="pText" select= 
    "substring-after($pText,',')"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template match= 
    "attr[generate-id() 
     = 
     generate-id(key('kAtrByVal',.)[1]) 
     ] 
    "> 
    <xsl:value-of select="concat('&#xA;',.,': ')"/> 

    <xsl:for-each select="key('kAtrByVal',.)"> 
    <xsl:value-of select="../@name"/> 
    <xsl:if test="not(position()=last())"> 
    <xsl:text>, </xsl:text> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template match="text()"/> 
</xsl:stylesheet> 

時所提供的XML文檔應用:

<items> 
    <item> 
     <name>item1</name> 
     <attributes>a,b,c,d</attributes> 
    </item> 
    <item> 
     <name>item2</name> 
     <attributes>c,d,e</attributes> 
    </item> 
</items> 

產生想要的,正確的結果:

a: item1 
b: item1 
c: item1, item2 
d: item1, item2 
e: item2 

說明

  1. 1步:標記化和最終結果是:

<groups> 
    <group name="item1"> 
    <attr>a</attr> 
    <attr>b</attr> 
    <attr>c</attr> 
    <attr>d</attr> 
    </group> 
    <group name="item2"> 
    <attr>c</attr> 
    <attr>d</attr> 
    <attr>e</attr> 
    </group> 
</groups> 

0.2。Pass2將Pass1的結果(使用擴展功能ext:node-set()轉換爲節點集)作爲輸入,執行Muenchian分組並生成最終的所需結果。

+0

這件事仍然有點麻煩。是否使用了',這樣當'被調用時,Muenchian分組模板可以做到這一點嗎? – ratherOCD 2011-04-06 16:00:59

+0

@ratherOCD:不,這個模板簡單地覆蓋了文本節點的XSLT內置模板,並確保文本節點不會被輸出爲的結果 – 2011-04-06 16:08:17

+0

謝謝,我得到了一切工作。有沒有一種按字母順序排列attr的好方法? – ratherOCD 2011-04-06 16:33:30

0

我的第一個想法是做兩遍。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/"> 
     <items> 
      <xsl:apply-templates/> 
     </items> 
    </xsl:template> 
    <xsl:template match="item"> 
     <item name="{name}"> 
      <xsl:apply-templates select="attributes"/> 
     </item> 
    </xsl:template> 
    <xsl:template match="attributes" name="tokenize"> 
     <xsl:param name="text" select="."/> 
     <xsl:param name="separator" select="','"/> 
     <xsl:choose> 
      <xsl:when test="not(contains($text, $separator))"> 
       <val> 
        <xsl:value-of select="normalize-space($text)"/> 
       </val> 
      </xsl:when> 
      <xsl:otherwise> 
       <val> 
        <xsl:value-of select="normalize-space(
         substring-before($text, $separator))"/> 
       </val> 
       <xsl:call-template name="tokenize"> 
        <xsl:with-param name="text" select="substring-after(
            $text, $separator)"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

哪個生產::

<items> 
    <item name="item1"> 
     <val>a</val> 
     <val>b</val> 
     <val>c</val> 
     <val>d</val> 
    </item> 
    <item name="item2"> 
     <val>c</val> 
     <val>d</val> 
     <val>e</val> 
    </item> 
</items> 

然後應用以下樣式表到輸出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:key name="byVal" match="val" use="." /> 
    <xsl:template match="val[generate-id() = 
          generate-id(key('byVal', .)[1])]"> 
     <xsl:value-of select="." /> 
     <xsl:text> : </xsl:text> 
     <xsl:apply-templates select="key('byVal', .)" mode="group" /> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:template> 
    <xsl:template match="val" mode="group"> 
     <xsl:value-of select="../@name" /> 
     <xsl:if test="position() != last()"> 
      <xsl:text>, </xsl:text> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template match="val" /> 
</xsl:stylesheet> 
首先,使用(略微)修改的 @Alejandro's answer to this previous question版本標記化的 attributes元件

生產時間:

a : item1 
b : item1 
c : item1, item2 
d : item1, item2 
e : item2 

在一個樣式表中做這件事需要更多的思考(或擴展功能)。

+0

+1從概念上來說是正確的。 – 2011-04-06 17:47:29