2010-06-07 37 views
4

我試圖使用XSLT來轉換文檔,方法是用整數id標記一組XML節點,從0開始,併爲組中的每個節點增加一個。傳遞給樣式表的XML應該被回顯出來,但是增加了這個額外的信息。如何使用XSLT使用唯一的,連續的,增加的整數ID來標記特定的節點?

只是要清楚什麼我說的,這裏是如何這種轉變將使用DOM表示:

states = document.getElementsByTagName("state"); 
for(i = 0; i < states.length; i++){ 
    states.stateNum = i; 
} 

這是非常簡單的使用DOM,但我有更多的麻煩這樣做與XSLT。我設計的當前策略是從身份轉換開始,然後創建一個全局變量,選擇並存儲我想要編號的所有節點。然後我創建一個匹配那種節點的模板。這個想法是,在模板中,我會查找匹配節點在全局變量nodelist中的位置,這會給我一個唯一的數字,然後我可以將它設置爲一個屬性。

這種方法的問題是,位置功能只能與上下文節點使用,所以像下面這樣是非法的:

<template match="state"> 
    <variable name="stateId" select="@id"/> 
    <variable name="uniqueStateNum" select="$globalVariable[@id = $stateId]/position()"/> 
</template> 

這同樣適用於以下情況:

<template match="state"> 
    <variable name="stateId" select="@id" 
    <variable name="stateNum" select="position($globalVariable[@id = $stateId])/"/> 
</template> 

爲了使用position()在$ globalVariable中查找元素的位置,必須更改上下文節點。

我找到了一個解決方案,但它是非常不理想的。基本上,在模板中,我使用for-each遍歷全局變量。 For-each改變上下文節點,所以這允許我以我描述的方式使用position()。問題是,這將通常將O(n)操作轉換爲O(n^2)操作,其中n是節點列表的長度,因爲無論何時匹配模板,都需要遍歷整個列表。我認爲必須有一個更優雅的解決方案。

總之,這裏是我目前(略簡體)XSLT樣式表:

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:s="http://www.w3.org/2005/07/scxml" 
    xmlns="http://www.w3.org/2005/07/scxml" 
    xmlns:c="http://msdl.cs.mcgill.ca/" 
    version="1.0"> 
    <xsl:output method="xml"/> 

    <!-- we copy them, so that we can use their positions as identifiers --> 
    <xsl:variable name="states" select="//s:state" /> 


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

    <xsl:template match="s:state"> 

     <xsl:variable name="stateId"> 
      <xsl:value-of select="@id"/> 
     </xsl:variable> 

     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 

      <xsl:for-each select="$states"> 
       <xsl:if test="@id = $stateId"> 
        <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
         <xsl:value-of select="position()"/> 
        </xsl:attribute> 
       </xsl:if> 
      </xsl:for-each> 

      <xsl:apply-templates select="node()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

我會很感激任何意見,任何人都可以提供。謝謝。

+0

好問題(+1)。看到我的答案是一個非常簡單但正確的解決方案。:) – 2010-06-07 23:08:42

回答

3

該轉化

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:s="http://www.w3.org/2005/07/scxml" 
> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

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

<xsl:template match="s:state"> 
    <xsl:variable name="vNum"> 
    <xsl:number level="any" count="s:state"/> 
    </xsl:variable> 

    <xsl:copy> 
    <xsl:copy-of select="@*"/> 

    <xsl:attribute name="stateId"> 
    <xsl:value-of select="@id"/> 
    </xsl:attribute> 

    <xsl:attribute name="id"> 
    <xsl:value-of select="$vNum -1"/> 
    </xsl:attribute> 

    <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

當所提供的XML文檔施加:

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state id="Compound1"> 
     <state id="Basic1"/> 
     <state id="Basic2"/> 
     <state id="Basic3"/> 
    </state> 
</scxml> 

產生想要的,正確的輸出

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state stateId="Compound1" id="0"> 
     <state stateId="Basic1" id="1"/> 
     <state stateId="Basic2" id="2"/> 
     <state stateId="Basic3" id="3"/> 
    </state> 
</scxml> 
+0

完美的作品,謝謝! – jbeard4 2010-06-08 00:36:00

0

最簡單的方法:

<xsl:template match="s:state"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
     <xsl:value-of select="count(preceding::s:state)" /> 
    </xsl:attribute> 
    <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
</xsl:template> 

不知道你的XSLT處理器如何處理preceding軸,所以這是值得基準在任何情況下。

+0

Tomalak,基於我對前面的軸的理解,這看起來像是一個合理的方法。不幸的是,它似乎並不奏效。請參見下面的XML文檔: \t <狀態ID = 「Compound1」> \t \t <狀態ID =「Basic1 「/> \t \t <狀態ID =」 Basic2 「/> \t \t <狀態ID =」 Basic3" /> \t 這將創建以下狀態號: STATEID:Compound1,stateNum:0 ; stateId:Basic1,stateNum:0; stateId:Basic2,stateNum:1; stateId:Basic3,stateNum:2 檢查Xalan,4xslt和xsltproc。 任何想法? – jbeard4 2010-06-07 20:38:03

+0

嗯。我明白你的意思了。嘗試添加'count(ancestor :: s:state)'和'count(之前的:: s:state)',但這也可能是錯誤的。現在沒有人需要測試,我現在正在使用我的手機。 ;-) – Tomalak 2010-06-07 20:49:40

相關問題