2015-06-24 39 views
0

我必須使用XSLT更新XML。XSLT:爲元素生成新的int唯一ID

我以前的XML元素有一些元素的屬性Id,其值爲int。某些元素沒有這Id,因爲它們不能被任何其他元素引用。

XML示例:

<Object Id="12359" Type="TypeX"> 
<Object Type="TypeA"> 
    <SomeOtherObject Id="112" Type="TypeV"/> 
</Object> 
<SomeOtherOtherObject Id="1596" Type="TypeM"/> 
</Object> 

對於一個特定項目(我可以輕鬆地管理使用XPath的要求來選擇,我要生成一個新的ID,這是不使用任何其他元素

可以這樣通過XSLT來完成

我已經有一些關鍵:

<xsl:key name="node-reference-key" match="*[@Id]" use="@Id" /> 

我可以選擇一個已經失蹤財產這樣的元素:

<xsl:template match="*[@Type='TypeA']"> 

這將通過一個C#應用程序運行,但我想不惜一切代價避免任何的C#代碼。

+0

,它必須得到一個整數XSLT有一個'產生-ID()'函數的唯一字符串標識符分配給每一個節點 –

+0

是的,這是一個。模型,目前在XML和數據庫中序列化,併發送給一些hardw是設備,沒有機會改變這個ID的類型 – J4N

+1

你應該真的告訴我們它是XSLT 1.0還是2.0。 –

回答

0

您不能修改XSLT中的變量,因此設置全局變量並在繼續跟蹤下一個唯一ID時遞增它是不可能的。

但你可以這樣做:

  • 選擇文檔中的最大ID:

    <xsl:variable name="max-id" select="//@Id[not(. &lt; //@Id)]" /> 
    
  • 創建一個逗號分隔的元素獨特的ID列表(與generate-id()的幫助)和一個已命名的模板:

    <xsl:variable name="missing-list"> 
        <xsl:call-template name="list-ids"> 
        <xsl:with-param name="nodes" select="//*[not(@Id)]" /> 
        </xsl:call-tempalte> 
    </xsl:variable> 
    
    <!-- and --> 
    
    <xsl:template name="list-ids"> 
        <xsl:param name="nodes" /> 
        <xsl:for-each select="$nodes"> 
        <xsl:value-of select="concat(generate-id(), ',')" /> 
        </xsl:for-each> 
    </xsl:template> 
    
  • 使用列表作爲參考點。對於任何給定的元素(即沒有0​​),新的唯一ID將在該列表中$max-id加上其位置(你可以得到與substring-before($missing-list, generate-id());獲得位置再算上逗號:

    <xsl:variable name="pos" select=" 
        string-length($preceding-ids) 
        - string-length(translate($preceding-ids, ',', '')) 
    "/> 
    
    <xsl:attribute name="Id"> 
        <xsl:value-of select="$max-id + $pos + 1" /> 
    </xsl:attribute> 
    

因此,放在一起,它看起來是這樣的:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output encoding="UTF-8" indent="yes" /> 

    <xsl:variable name="max-id" select="//@Id[not(. &lt; //@Id)]" /> 

    <xsl:variable name="missing-list"> 
     <xsl:call-template name="list-ids"> 
     <xsl:with-param name="nodes" select="//*[not(@Id)]" /> 
     </xsl:call-template> 
    </xsl:variable> 

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

    <xsl:template match="*[not(@Id)]"> 
     <xsl:copy> 
     <xsl:apply-templates select="@*" /> 

     <xsl:variable name="preceding-ids" select=" 
      substring-before($missing-list, generate-id()) 
     "/> 
     <xsl:variable name="pos" select=" 
      string-length($preceding-ids) 
      - string-length(translate($preceding-ids, ',', '')) 
     "/> 

     <xsl:attribute name="Id"> 
      <xsl:value-of select="$max-id + $pos + 1" /> 
     </xsl:attribute> 

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

    <xsl:template name="list-ids"> 
     <xsl:param name="nodes" /> 
     <xsl:for-each select="$nodes"> 
     <xsl:value-of select="concat(generate-id(), ',')" /> 
     </xsl:for-each> 
    </xsl:template>   
</xsl:transform> 
+0

我明白你的觀點。我認爲在大多數情況下,它會起作用。我可能會遇到的問題是,我的ID可能不是連續的,而是隨機的(不是我的選擇:/),儘管我已經在最大使用ID後有足夠的ID,但應該沒問題,但我不確定我可以指望它:( – J4N

+0

@ J4N那麼,我的假設當然是,你的ID不會侷限於最大值,並且在你的用例中溢出不太可能。 – Tomalak

+0

好的,謝謝你。你是對的,這是非常不可能的。這只是根據墨菲定律,因爲如果出現錯誤,我們可能會丟失重要數據,所以我試圖找到最好的方法。但好處是,我知道不會有很多項目可以添加此Id。一個問題,是否有可能在C#中使用XSLT標籤進行編碼? – J4N