【问题标题】:Generate sequence number in XSLT在 XSLT 中生成序列号
【发布时间】:2019-08-01 09:22:04
【问题描述】:

我有一个源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 生成响应。想基于&lt;number&gt; 元素生成&lt;line&gt; 元素。例如:对于每一个相同的&lt;number&gt; 想生成序列号。目标文件必须按如下方式生成:

<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>

【问题讨论】:

  • 好问题!存在一个非常简短的 XSLT 1.0 解决方案。

标签: xml xslt-1.0 xslt-2.0


【解决方案1】:

transaction 元素按numberfor-each-group 分组,然后将current-group() 推送到apply-templates,然后在transaction 的模板中,您可以使用@987654332 的值填充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

上面将在结果中将具有相同 number 值的 transaction 元素组合在一起,如果不需要,那么在 XSLT 3 中另一个选项是使用累加器来记录 transaction 元素number 的值并输出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

两个完整示例都使用 &lt;xsl:mode on-no-match="shallow-copy"/&gt; 的 XSLT 3 方式将身份转换声明为转换的基础,对于 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

【讨论】:

  • 嗨 Martin,我使用的是 xslt2.0,但在 2.0 中 current-group() 函数不存在。
  • XSLT 2 于 2007 年推出,它确实支持 xsl:for-each-group 以及 current-group 功能。所以不知道为什么你声称你使用 XSLT 2 但不支持该功能。 Saxon 9、XmlPrime、AltovaXML 等处理器支持 XSLT 2。
  • 嗨 Martin,我正在使用使用 xslt2.0 的 oracle ICS,这就是原因,我想要一个与 xslt 2.0 兼容的解决方案
  • 正如我所说,对我来说 XSLT 2 是 W3C 在 2007 年指定的内容,并且该规范 w3.org/TR/xslt20/#current-group 确实定义了我使用的函数。如果您使用不支持它的处理器,那么我不会将其称为 XSLT 2 处理器。在 Java 世界中,Saxon 9 可用于 XSLT 2 或 3。不确定它如何适合您的环境,但这是一个不同的问题。如果您需要 XSLT 1 解决方案或特定处理器的 XSLT 解决方案,请适当地重新标记和改写您的问题。
  • 我添加了一个使用键和 Muechian 分组的 XSLT 1 示例,它的结果应该与使用 for-each-groupcurrent-group() 的 XSLT 2/3 方法相同。
【解决方案2】:

这是一个简短的 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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    相关资源
    最近更新 更多