使用 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)}">
如果可能,我需要编写一个 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">
但是,我一直在尝试使用
目标格式对ids有两个限制:每个id属性,不管
有什么方法可以通过 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)}">