2016-11-24 81 views
0

說我有這樣的XML:避免重複屬性的名稱與XSLT/XPath的

<parole> 
    <parola id="a">1</parola> 
    <parola id="b">2</parola> 
    <parola id="c">3</parola> 
    <parola id="a">4</parola> 
    <parola id="a">5</parola> 
    <parola id="b">6</parola> 
</parole> 

現在,我知道generate-id()函數存在。但是,爲了學習目的,我想知道如何使用XSLT更改名爲「id」的屬性值。

我想過的「算法」,如:

考慮以下和屬性的前面的兄弟。
如果您遇到了當前屬性的副本,請在該名稱的末尾添加「f」並遞歸添加另一個「f」,直到我們沒有更多具有相同值的屬性。

所以我最後的XML會是這樣:

<parole> 
    <parola id="a">1</parola> 
    <parola id="b">2</parola> 
    <parola id="c">3</parola> 
    <parola id="af">4</parola> 
    <parola id="aff">5</parola> 
    <parola id="bf">6</parola> 
</parole> 

現在,我試圖得到這樣的結果與像一個遞歸函數:

<xsl:variable name="following-siblings-ids" select="/parole/parola/following-sibling::parola/@id"/> 
<xsl:variable name="preceding-siblings-ids" select="/parole/parola/preceding-sibling::parola/@id"/> 

<xsl:function name="du:check" as="xs:string"> 
    <xsl:param name="id" /> 
    <xsl:choose> 
     <xsl:when test="$id = $following-siblings-ids and $preceding-siblings-ids"> 
      <xsl:value-of select="du:check(concat($id, 'f'))"/> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:value-of select="$id"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:function> 

<xsl:template match="parola"> 
    <xsl:value-of select="du:check(@id)"/> 
</xsl:template match="parola"> 

但我得到這樣一個結果

「AF」, 「BF」, 「CF」, 「AF」, 「AF」, 「BF」

而不是所需的一個。

任何提示?這將是一個非動態變量和for-each的非聲明性語言的簡單任務,但我不知道如何在XSLT中實現它...

+0

應該不是你的樣本文檔中的第一行有ID * A *,而不是* AF *? – Markus

+0

您應該計算前面的@ id與當前相同,並在最後添加許多f。你可能沒有碰到的地方是你寫的地方(「遞歸地)添加另一個f」,這意味着你必須在前一次迭代的結果上運行* n *次(但是在你的邏輯中可能存在另一個錯誤) –

+0

是的,已更正,@Markus – AmintaCode

回答

1

這是一個XSLT 1解決方案。下面轉型

<xsl:stylesheet 
    version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

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

    <xsl:template name="make-f"> 
    <xsl:param name="n"/> 
    <xsl:if test="$n > 0">f<xsl:call-template name="make-f"> 
    <xsl:with-param name="n" select="$n - 1"/></xsl:call-template> 
    </xsl:if> 
    </xsl:template> 

    <xsl:template match="parola/@id"> 
    <xsl:variable name="id" select="."/> 
    <xsl:variable name="f"> 
     <xsl:call-template name="make-f"> 
    <xsl:with-param name="n" select="count(../preceding-sibling::parola/@id[.=$id])"/> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:attribute name="id"> 
     <xsl:value-of select="concat(., $f)"/> 
    </xsl:attribute> 
    </xsl:template> 

</xsl:stylesheet> 

當應用到你的輸入樣本,輸出如下:

$ xsltproc test.xsl test.xml 
<?xml version="1.0"?> 
<parole> 
    <parola id="a">1</parola> 
    <parola id="b">2</parola> 
    <parola id="c">3</parola> 
    <parola id="af">4</parola> 
    <parola id="aff">5</parola> 
    <parola id="bf">6</parola> 
</parole> 
0

我會用一個鍵來「分組」元素id屬性,然後確定該組中的位置追加一個索引,或者如果你想的f字母數:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:mf="http://example.com/mf" 
    exclude-result-prefixes="xs mf" 
    version="2.0"> 

    <xsl:param name="n" select="'f'"/> 

    <xsl:function name="mf:node-index-of" as="xs:integer"> 
     <xsl:param name="node" as="node()"/> 
     <xsl:param name="sequence" as="node()*"/> 
     <xsl:sequence select="for $pos in 1 to count($sequence) return $pos[$node is $sequence[$pos]]"/> 
    </xsl:function> 

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

    <xsl:key name="p-by-id" match="parola" use="@id"/> 

    <xsl:template match="parola[not(. is key('p-by-id', @id)[1])]/@id"> 
     <xsl:attribute name="id" select="concat(., string-join(for $i in 1 to mf:node-index-of(.., key('p-by-id', .)) - 1 return $n, ''))"/> 
    </xsl:template> 

</xsl:stylesheet> 

match="@* | node()"模板是水平的所謂身份轉換模板拷貝一切等級不變,允許我們爲我們想要轉換的節點添加更多的模板。

要轉換的唯一節點,其中一個以上具有值某個ID存在parola元素id屬性,所以我寫了一個模板match="parola[not(. is key('p-by-id', @id)[1])]/@id",使用密鑰<xsl:key name="p-by-id" match="parola" use="@id"/>識別具有相同的所有元素值。

然後通過將現有值與包含其組中的父親parola的位置索引的次數相比少的序列連接來計算新屬性值concat(., string-join(for $i in 1 to mf:node-index-of(.., key('p-by-id', .)) - 1 return $n, ''))

如果您不熟悉XSLT和XPath 2.0,請花些時間閱讀關於身份轉換,密鑰和用戶定義函數的教程或書籍。

+0

請你能解釋一下你的解決方案嗎?我會非常感謝@馬丁 - 亨寧 – AmintaCode