XSLT:为元素

时间:2015-06-24 14:53:07

标签: xml xslt xpath

我使用XSLT更新XML。

我之前的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#代码。

1 个答案:

答案 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>
    
  • 使用该列表作为参考点。对于任何给定元素(没有@Id),新的唯一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>