我有一个合并的XML文件(为简单起见)具有以下形式:
<bookstore>
<books>
<book id="1"/>
<book id="2"/>
<book id="2"/>
<book id="3"/>
<book id="10"/>
</books>
</bookstore>
由于XML已合并,因此存在具有相同ID属性的书籍。我需要遵循以下规则使ID唯一:如果遇到 ID (从上到下),则将此ID更改为 MAX(ID)+1
<bookstore>
<books>
<book id="1"/>
<book id="2"/>
<book id="11"/>
<book id="3"/>
<book id="10"/>
</books>
</bookstore>
这样做的直接方法是提取ID,检查它们的出现次数,如果它出现多次,则搜索ID的第二次出现(从上到下)并替换它。但这不是很优雅......
当我正在阅读有关XML处理的内容时,我希望有一个(简单的)XQuery可以做到这一点。 如果有人有一些指针或伪代码:他们都欢迎。
我的环境是Oracle(PL)SQL数据库,支持 XMLTYPE 和 XQuery 。
答案 0 :(得分:2)
在您的环境中,您可以使用XSLT 1.0转换文档并在此过程中生成ID。请参阅:DBMS_XSLPROCESSOR。
使用XSLT样式表,您可以将XML源中的节点复制到结果树,从而在流程中创建唯一ID。 ID不是序列号,而是generate-id()
方法生成的唯一字符串序列。你无法控制它们的样子,但你可以保证它们是独一无二的。 (如果这是你的意图,XSLT还允许你摆脱重复的节点(使用键),但从你的例子我明白重复的* 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>
其他模板(在本例中仅为标识模板)将针对所有节点和属性进行调用,除 id
属性 <xsl:apply-templates ...>
之外。结果是原始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()
来生成基于元素位置的数字在整个文档中(这将是唯一的)。如果所有图书都是同一上下文中的兄弟姐妹,则只需替换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>
(如果书籍不是兄弟姐妹,你需要以稍微不同的方式使用变量)。
如果你想保留现有的ID 并且只为重复项生成连续的ID,那么它会更复杂,但你可以通过键(或XQuery而不是XSLT)来实现。可以使用id
函数在XPath 2.0中获取最大max()
:
max(//book/@id)
XPath 1.0中不存在该函数,但您可以使用以下命令获取最大ID:
//book[not(@id < //book/@id)]/@id