【问题标题】:XSLT: Generate a new int unique ID for an elementXSLT:为元素生成一个新的 int 唯一 ID
【发布时间】:2015-06-24 23:49:45
【问题描述】:

我必须使用 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# 代码。

【问题讨论】:

  • 必须是整数吗? XSLT 有一个generate-id() 函数来为每个节点分配一个唯一的字符串标识符。
  • 是的,这是一个模型,目前是在XML中序列化,在数据库中,发送到一些硬件设备,没有机会改变这个id的类型
  • 你真的应该告诉我们它是 XSLT 1.0 还是 2.0。

标签: xml xslt xpath


【解决方案1】:

您无法在 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>

【讨论】:

  • 我明白你的意思。我认为在大多数情况下它会起作用。我可能遇到的问题是我的 ID 可能不是连续的而是随机的(不是我的选择:/),就我在最大使用的 ID 之后有足够的 ID 可用,应该没问题,但我不确定我可以指望它:(
  • @J4N 好吧,我当然假设您的 ID 不会被限制在最大值,并且在您的用例中不太可能发生溢出。
  • 好的,谢谢。你是对的,这不太可能。墨菲定律就是这样,如果出现问题,我们可能会丢失重要数据,所以我试图找到最好的方法。但好在我知道添加这个Id的项目不会很多。一个问题,是否可以在 C# 中编写 XSLT 标记?
  • 我不明白这个问题。如果你问 C# 是否可以运行 XSLT 代码,是的。
  • 不,如果我能做到这一点,更多:&lt;custom:id-generator-generate other-element-match="*/@Id" /&gt; 这将是 c# 中的代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 2013-04-19
  • 2013-04-11
  • 1970-01-01
  • 2015-01-30
  • 1970-01-01
相关资源
最近更新 更多