使用 XSL 生成 id 系统

Generate id system with XSL

如果可能,我需要编写一个 XSL 文件来转换类似于此

的 XML 语法
<media address="1234 A St.">
  <book title="My Book" isbn="1324-1123-1456-1566" />
  <book title="Your Book" isbn="1232-1123-1456-1566" />
</media>

转换成这样的格式

<library>

    <information>
       <building id="1">
           <address>1234 A St.</address>
       </building>
    </information>

    <medialist>
        <book_definitions>
            <book_definition id="2" />
            <book_definition id="3" />
        </book_definitions>
        <book_metadata>
           <metadata id="4">
               <isbn>1324-1123-1456-1566</isbn>
               <book_definition_id>2</book_definition_id>
           </metadata>
           <metadata id="5">
               <isbn>1232-1123-1456-1566</isbn>
               <book_definition_id>3</book_definition_id>
           </metadata>
        </book_metadata>
        <book_instances>
            <book_instance id="6">
                <book_definition_id>2</book_definition_id>
                <book_metadata_id>4</book_metadata_id>
                <title>My Book</title>
            </book_instance>
            <book_instance id="7">
                <book_definition_id>2</book_definition_id>
                <book_metadata_id>5</book_metadata_id>
                <title>Your Book</title>
            </book_instance>
        </book_instances>
    </medialist>
</library>

我意识到目标格式有点复杂,但我无法控制它。

我已成功编写 XSL 以使用模板模式正确转换大多数 XML 标记。即

<xsl:template match="/media/book" mode="definitions">
<xsl:template match="/media/book" mode="metadata">
<xsl:template match="/media/book" mode="instance">

但是,我一直在尝试使用 或其他一些技巧来正确生成 ID,但收效甚微。

目标格式对ids有两个限制:每个id属性,不管,name必须是唯一的。 id 一旦排序,必​​须是顺序的,但它们可以以目标格式的任何顺序出现,即 (1,2,3,4,5) 和 (5,2,3,1,4) 都是可以接受的,但是 ( 1,2,4,5,6) 不是。

有什么方法可以通过 XSL 来实现吗?

在我看来,因为(除了建筑物),你总是为每本书为每个不同的 "kind" 节点生成一个 ID,你可以通过生成它们来保证唯一和顺序的 ID一个公式。如果你有 N 本书,那么你可以 "decree" 那

  • 建筑物始终为 id 1
  • 第 m 本书的 book_definition 总是 1+m(所以 运行 从 2 到 N+1)
  • 第 m 本书的元数据是 (N+1)+m
  • 实例是(2N+1)+m

如果你到处都遵循这个方案,你可以简单地计算出适当的交叉引用 book_definition_id 等,而不需要查找 table.

虽然我刚刚注意到 Ian Roberts 已经解释了这种方法,但我已经准备为此编写一个 XSLT,所以无论如何我都会 post 它 - 按照 XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
  <xsl:strip-space elements="*" />
    <xsl:template match="/">
        <library>
            <xsl:apply-templates />
        </library>
    </xsl:template>
    <xsl:template match="media">
        <xsl:variable name="current">
            <xsl:number />
        </xsl:variable>
        <information>
            <building>
                <xsl:attribute name="id">
                    <xsl:value-of select="$current" />
                </xsl:attribute>
                <address>
                    <xsl:value-of select="@address" />
                </address>
            </building>
        </information>
        <medialist>
            <book_definitions>
                <xsl:apply-templates mode="definition">
                    <xsl:with-param name="current">
                        <xsl:value-of select="$current" />
                    </xsl:with-param>
                </xsl:apply-templates>
            </book_definitions>
            <book_metadata>
                <xsl:apply-templates mode="metadata">
                    <xsl:with-param name="current">
                        <xsl:value-of select="$current" />
                    </xsl:with-param>
                </xsl:apply-templates>
            </book_metadata>
            <book_instances>
                <xsl:apply-templates mode="instances">
                    <xsl:with-param name="current">
                        <xsl:value-of select="$current" />
                    </xsl:with-param>
                </xsl:apply-templates>
            </book_instances>
        </medialist>
    </xsl:template>
    <xsl:template match="book" mode="definition">
        <xsl:param name="current" />
        <book_definition id="{(position() + $current)}" />
    </xsl:template>
    <xsl:template match="book" mode="metadata">
        <xsl:param name="current" />
        <metadata id="{(position() + $current + count(parent::media/book))}">
            <isbn>
                <xsl:value-of select="@isbn" />
            </isbn>
            <book_definition_id>
                <xsl:value-of select="position() + $current" />
            </book_definition_id>
        </metadata>
    </xsl:template>
    <xsl:template match="book" mode="instances">
        <xsl:param name="current" />
        <book_instance id="{(position() + 2*count(parent::media/book) + $current)}">
            <book_definition_id>
                <xsl:value-of select="position() + $current" />
            </book_definition_id>
            <book_metadata_id>
                <xsl:value-of select="position() + $current + count(parent::media/book)" />
            </book_metadata_id>
            <title>
                <xsl:value-of select="@title" />
            </title>
        </book_instance>
    </xsl:template>
</xsl:transform>

当应用于您的输入时 XML 生成输出

<library>
 <information>
   <building id="1">
     <address>1234 A St.</address>
   </building>
 </information>
 <medialist>
    <book_definitions>
      <book_definition id="2"/>
      <book_definition id="3"/>
    </book_definitions>
    <book_metadata>
      <metadata id="4">
        <isbn>1324-1123-1456-1566</isbn>
        <book_definition_id>2</book_definition_id>
      </metadata>
      <metadata id="5">
        <isbn>1232-1123-1456-1566</isbn>
        <book_definition_id>3</book_definition_id>
      </metadata>
    </book_metadata>
    <book_instances>
      <book_instance id="6">
        <book_definition_id>2</book_definition_id>
        <book_metadata_id>4</book_metadata_id>
        <title>My Book</title>
      </book_instance>
      <book_instance id="7">
        <book_definition_id>3</book_definition_id>
        <book_metadata_id>5</book_metadata_id>
        <title>Your Book</title>
      </book_instance>
    </book_instances>
  </medialist>
</library>

因为不清楚输入 XML 是否由多个 media 元素组成 - 例如下一个建筑物应该以 id 8 开头 - 我使用数字作为参数而不是仅仅添加 1.
请注意,此模板不适用于第二个 media 元素 - 这只会以 2 作为 id 的值开始 - 并且必须相应地进行调整以防实际输入 XML包含多个建筑物/media 个元素。
对于图书定义,id为当前图书位置与参数值current:

之和
<book_definition id="{(position() + $current)}" /> 

元数据id是当前图书的位置、当前/父media元素的所有图书和current:

的总和
<metadata id="{(position() + $current + count(parent::media/book))}">

而图书实例id,考虑到之前为元数据生成的id,是当前图书位置的总和,current和父media元素的所有图书* 2:

<book_instance id="{(position() + 2*count(parent::media/book) + $current)}">