我使用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#代码。
答案 0 :(得分:0)
您无法修改XSLT中的变量,因此无法设置全局变量并在继续跟踪下一个唯一ID时将其递增。
但你可以这样做:
选择文档中的最大ID:
<xsl:variable name="max-id" select="//@Id[not(. < //@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(. < //@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>