使用XSL生成id系统

时间:2015-02-24 17:23:45

标签: xml xslt

如果可能,我需要编写一个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">

但是,我一直在尝试使用&lt; xsl:number&gt;或者其他一些技巧来正确生成id,但收效甚微。

目标格式对id有两个约束:每个id属性,不管&lt; element&gt;,name必须是唯一的。一旦排序,ID必须是顺序的,但它们可以按目标格式的任何顺序出现,即(1,2,3,4,5)和(5,2,3,1,4)都可以接受但是( 1,2,4,5,6)不是。

有没有办法通过XSL实现这一目标?

2 个答案:

答案 0 :(得分:1)

在我看来,由于(除了建筑物),你总是为每个不同的“种类”节点为每本书生成一个ID,你可以通过根据公式生成它们来保证唯一和顺序id。如果你有N本书,那么你可以“判决”

  • 建筑物总是id 1
  • 图书m的book_definition始终为1+m(因此从2上升到N + 1)
  • 图书m的元数据为(N+1)+m
  • 实例为(2N+1)+m

如果您在任何地方都遵循此方案,您只需计算相应的交叉引用book_definition_id等,而无需查找表。

答案 1 :(得分:1)

虽然我刚刚注意到Ian Roberts已经解释过这种方法,但我已经准备为此编写一个XSLT,所以我会发布它 - 无论是否遵循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元素 - 这只是以id的值2开头 - 如果实际输入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)}">