在 XSLT 中生成序列号



我有一个源xml文件,如下所示:

<transactions>
<transaction>
<number>12</number>
</transaction>
<transaction>
<number>12</number>
</transaction>
<transaction>
<number>13</number>
</transaction>
<transaction>
<number>13</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
</transactions>

想要使用 XSLT 生成响应。想要基于<number>元素生成<line>元素。例如:对于每个相同的<number>都希望生成序列号。目标文件必须生成如下:

<transactions>
<transaction>
<number>12</number>
<line>1</line>
</transaction>
<transaction>
<number>12</number>
<line>2</line>
</transaction>
<transaction>
<number>13</number>
<line>1</line>
</transaction>
<transaction>
<number>13</number>
<line>2</line>
</transaction>
<transaction>
<number>14</number>
<line>1</line>
</transaction>
<transaction>
<number>14</number>
<line>2</line>
</transaction>
<transaction>
<number>14</number>
<line>3</line>
</transaction>
</transactions>

使用for-each-groupnumbertransaction元素进行分组,然后将current-group()推送到apply-templates,然后在模板中transaction您可以使用position()的值填充line元素:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="transactions">
<xsl:copy>
<xsl:for-each-group select="transaction" group-by="number">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="transaction">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
<line>
<xsl:value-of select="position()"/>
</line>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyRYYjm

上面会在结果中将transaction具有相同number值的元素组合在一起,如果不需要,那么在 XSLT 3 中,另一种选择是使用累加器通过number值来记录transaction元素,并在模板中输出累加器值以供transaction

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="trans-count"/>
<xsl:accumulator name="trans-count" as="map(xs:integer, xs:integer)" initial-value="map{}">
<xsl:accumulator-rule match="transaction"
select="let $number := xs:integer(number)
return if (map:contains($value, $number))
then map:put($value, $number, $value($number) + 1)
else map:put($value, $number, 1)"/>
</xsl:accumulator>
<xsl:template match="transaction">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
<line>
<xsl:value-of select="accumulator-before('trans-count')(xs:integer(number))"/>
</line>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyRYYjm/1

两个完整示例都使用 XSLT 3<xsl:mode on-no-match="shallow-copy"/>方式将标识转换声明为转换的基础,对于 XSLT 2 处理器,您需要将其拼写为

<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>

相反。

对于使用键和 Muenchian 分组的 XSLT 1 解决方案,您可以使用

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="trans-group"
match="transaction" use="number"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="transactions">
<xsl:copy>
<xsl:for-each select="transaction[generate-id() = generate-id(key('trans-group', number)[1])]">
<xsl:apply-templates select="key('trans-group', number)"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="transaction">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
<line>
<xsl:value-of select="position()"/>
</line>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyRYYjm/4

这是一个简短而简单的 XSLT 1.0 解决方案 -- 只有 15 行(适用于 XSLT 2.0 或 3.0 处理器,因为 XSLT 是向后兼容的):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="number">
<xsl:call-template name="identity"/>
<line><xsl:value-of select="count(../preceding-sibling::*[number = current()]) + 1"/></line>
</xsl:template>
</xsl:stylesheet>

在提供的 XML 文档上应用此转换时

<transactions>
<transaction>
<number>12</number>
</transaction>
<transaction>
<number>12</number>
</transaction>
<transaction>
<number>13</number>
</transaction>
<transaction>
<number>13</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
<transaction>
<number>14</number>
</transaction>
</transactions>

生成所需的正确结果

<transactions>
<transaction>
<number>12</number>
<line>1</line>
</transaction>
<transaction>
<number>12</number>
<line>2</line>
</transaction>
<transaction>
<number>13</number>
<line>1</line>
</transaction>
<transaction>
<number>13</number>
<line>2</line>
</transaction>
<transaction>
<number>14</number>
<line>1</line>
</transaction>
<transaction>
<number>14</number>
<line>2</line>
</transaction>
<transaction>
<number>14</number>
<line>3</line>
</transaction>
</transactions>

相关内容

  • 没有找到相关文章

最新更新