在XSLT 2.0转换中更改行号

时间:2019-03-07 19:46:12

标签: xml xslt xslt-2.0

在解决客户对其XML转换的请求时,我遇到了一些麻烦。他们要求收到重复的发票行时,希望自动将重复行的值更改为发票行号序列中的下一个值,请参见下面的输入和预期输出。

输入:

<?xml version="1.0" encoding="UTF-8"?>
    <DatiBeniServizi>
        <DettaglioLinee>
            <NumeroLinea>1</NumeroLinea>
        </DettaglioLinee>
        <DettaglioLinee>
            <NumeroLinea>1</NumeroLinea>
        </DettaglioLinee>
        <DettaglioLinee>
            <NumeroLinea>2</NumeroLinea>
        </DettaglioLinee>
        <DettaglioLinee>
            <NumeroLinea>3</NumeroLinea>
        </DettaglioLinee>
    </DatiBeniServizi>

预期输出:

<Invoice>
        <InvoiceDetail>
            <InvoiceLineNumber>1</InvoiceLineNumber>
        </InvoiceDetail>
        <InvoiceDetail>
            <InvoiceLineNumber>4</InvoiceLineNumber>
        </InvoiceDetail>
        <InvoiceDetail>
            <InvoiceLineNumber>2</InvoiceLineNumber>
        </InvoiceDetail>
        <InvoiceDetail>
            <InvoiceLineNumber>3</InvoiceLineNumber>
        </InvoiceDetail>
</Invoice>

我尝试分组,但没有得到他们要求的结果,这听起来很简单,但我整天都在徒劳地解决问题。

欢迎任何帮助,感谢您的宝贵时间!

2 个答案:

答案 0 :(得分:2)

假设每个重复项每次都增加1(因此第一个重复项是4,然后是下一个5),执行此操作的一种方法可能是使用递归模板,该模板依次处理每个项目并递增一个参数当发现重复项时。

要查找重复项,我不得不使用Muenchian分组,该分组通常在XSLT 1.0中使用

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" />

  <xsl:key name="invoices" match="DettaglioLinee" use="NumeroLinea" />

  <xsl:variable name="max" select="max(/DatiBeniServizi/DettaglioLinee/NumeroLinea)" />

  <xsl:template match="DatiBeniServizi">
    <Invoice>
      <xsl:apply-templates select="DettaglioLinee[1]" />
    </Invoice>
  </xsl:template>

  <xsl:template match="DettaglioLinee">
    <xsl:param name="incr" select="1" />

    <xsl:variable name="isDistinct" select="generate-id() = generate-id(key('invoices', NumeroLinea)[1])" />
    <InvoiceDetail>
      <InvoiceLineNumber>
        <xsl:value-of select="if ($isDistinct) then NumeroLinea else $max + $incr" />
      </InvoiceLineNumber>
    </InvoiceDetail>
    <xsl:apply-templates select="following-sibling::*[1]">
      <xsl:with-param name="incr" select="if ($isDistinct) then $incr else $incr + 1" />
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

如果重复项总是连续的(例如,您将没有1,2,1),则删除键的使用,并定义isDistinct变量,如下所示:

<xsl:variable name="isDistinct" select="not(NumeroLinea = preceding-sibling::*[1]/NumeroLinea)" />

编辑:如果您不担心数字是连续的(例如,可以有1、5、2、3),则只需将重复项的位置添加到最大值上,这样可以避免重复

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" />

  <xsl:key name="invoices" match="DettaglioLinee" use="NumeroLinea" />

  <xsl:variable name="max" select="max(/DatiBeniServizi/DettaglioLinee/NumeroLinea)" />

  <xsl:template match="DatiBeniServizi">
    <Invoice>
      <xsl:apply-templates select="DettaglioLinee" />
    </Invoice>
  </xsl:template>

  <xsl:template match="DettaglioLinee">
    <xsl:variable name="isDistinct" select="generate-id() = generate-id(key('invoices', NumeroLinea)[1])" />
    <InvoiceDetail>
      <InvoiceLineNumber>
        <xsl:value-of select="if ($isDistinct) then NumeroLinea else $max + position()" />
      </InvoiceLineNumber>
    </InvoiceDetail>
  </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

这是将分组问题与编号问题混合在一起。

此XSLT 1.0样式表

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kNumeroLineaByValue" match="NumeroLinea" use="." />
    <xsl:variable name="vNumeroLineaDistinct"
        select="//NumeroLinea[generate-id()=generate-id(key('kNumeroLineaByValue',.))]" />
    <xsl:variable name="vNumeroLineaDistinctCount"
        select="count($vNumeroLineaDistinct)" />
    <xsl:variable name="vNumeroLineaLastValue">
        <xsl:for-each select="$vNumeroLineaDistinct">
            <xsl:sort data-type="number" order="descending" />
            <xsl:if test="position()=1">
                <xsl:value-of select="." />
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="NumeroLinea/text()">
        <xsl:choose>
            <xsl:when
                test="count(..|$vNumeroLineaDistinct)!=$vNumeroLineaDistinctCount">
                <xsl:variable name="vPosition">
                    <xsl:number level="any"
                        count="NumeroLinea[.=preceding::NumeroLinea]" />
                </xsl:variable>
                <xsl:value-of
                    select="$vPosition + $vNumeroLineaLastValue" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

此输入

<DatiBeniServizi>
    <DettaglioLinee>
        <NumeroLinea>1</NumeroLinea>
    </DettaglioLinee>
    <DettaglioLinee>
        <NumeroLinea>1</NumeroLinea>
    </DettaglioLinee>
    <DettaglioLinee>
        <NumeroLinea>2</NumeroLinea>
    </DettaglioLinee>
    <DettaglioLinee>
        <NumeroLinea>3</NumeroLinea>
    </DettaglioLinee>
    <DettaglioLinee>
        <NumeroLinea>3</NumeroLinea>
    </DettaglioLinee>
    <DettaglioLinee>
        <NumeroLinea>5</NumeroLinea>
    </DettaglioLinee>
</DatiBeniServizi>

结果:

<DatiBeniServizi>
      <DettaglioLinee>
            <NumeroLinea>1</NumeroLinea>
      </DettaglioLinee>
      <DettaglioLinee>
            <NumeroLinea>6</NumeroLinea>
      </DettaglioLinee>
      <DettaglioLinee>
            <NumeroLinea>2</NumeroLinea>
      </DettaglioLinee>
      <DettaglioLinee>
            <NumeroLinea>3</NumeroLinea>
      </DettaglioLinee>
      <DettaglioLinee>
            <NumeroLinea>7</NumeroLinea>
      </DettaglioLinee>
      <DettaglioLinee>
            <NumeroLinea>5</NumeroLinea>
      </DettaglioLinee>
</DatiBeniServizi>

注意:分组进行重复数据删除,然后从起始值(所有不同值的最大值)开始编号