在您的环境中,您可以使用 XSLT 1.0 转换文档并在此过程中生成 ID。请参阅:DBMS_XSLPROCESSOR。
使用 XSLT 样式表,您可以将节点从 XML 源复制到结果树,在此过程中创建唯一 ID。 ID 不是序列号,而是由generate-id() 方法生成的唯一字符串序列。你无法控制它们的外观,但你可以保证它们是独一无二的。 (如果这是您的意图,XSLT 还允许您删除重复节点(使用 key),但从您的示例中我了解到重复的 *ID*s 实际上并不意味着节点是重复的,因为您想为其生成一个新 ID。)
下面的样式表有两个模板。第二个是恒等变换:它只是将元素和属性复制到结果树。第一个模板创建一个名为 id 的属性,其中包含一个唯一 ID。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="book">
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="generate-id(.)"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*[name() != 'id']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
为所有节点和属性调用其他模板(在这种情况下只有标识模板),除了<xsl:apply-templates ...> 的id 属性。结果是原始 XML 文件的副本,其中包含为 book 元素生成的唯一 ID。
如果您有这样的 XML:
<bookstore>
<books>
<book id="1" other="123"/>
<book id="2"/>
<book id="2"/>
<book id="3">
<chapter number="123" id="ch1">Text</chapter>
</book>
<book id="10"/>
</books>
<magazines>
<mag id="non-book-id"></mag>
</magazines>
</bookstore>
上面的 XSLT 会将它转换成这个 XML:
<bookstore>
<books>
<book id="d2e3" other="123"/>
<book id="d2e4"/>
<book id="d2e5"/>
<book id="d2e6">
<chapter number="123" id="ch1">Text</chapter>
</book>
<book id="d2e9"/>
</books>
<magazines>
<mag id="non-book-id"/>
</magazines>
</bookstore>
(字符串序列是任意的,在您的实现中可能会有所不同)。
对于创建 ID/IDREF 链接,生成的字符串序列比数字更好,因为您可以在任何地方使用它们(以数字开头的数字和标识符不能始终用作 ID)。但是如果字符串序列不可接受并且您需要连续数字,则可以使用 XQuery 或 XSLT 中的 XPath 节点 position() 根据元素在整个文档中的位置生成数字 em> (这将是唯一的)。如果所有书籍都是同一个上下文中的兄弟,你可以简单地将上面样式表中的generate-id(.)替换为position():
<xsl:template match="book">
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="position()"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*[name() != 'id']"/>
</xsl:copy>
</xsl:template>
(如果 books 不是同级的,则需要以稍微不同的方式进行操作,使用变量)。
如果您想保留现有的 ID 并且只为重复的 ID 生成顺序 ID,这会有点复杂,但您可以使用键(或 XQuery 而不是 XSLT)来实现。在 XPath 2.0 中使用max() 函数可以获得最大的id:
max(//book/@id)
XPath 1.0 中不存在该功能,但您可以通过以下方式获取最大 ID:
//book[not(@id < //book/@id)]/@id