【问题标题】:Changing line numbers in XSLT 2.0 transformation在 XSLT 2.0 转换中更改行号
【发布时间】:2019-07-29 19:24:06
【问题描述】:

我在解决客户对其 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>

我尝试分组,但我没有得到他们要求的结果,这听起来很容易,但我花了一整天的时间试图解决。

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

【问题讨论】:

  • 请解释它背后的逻辑是什么......你有1,1,2,3和输出1,4,2,3。例子不清楚。
  • 抱歉,我会编辑问题。逻辑应该是:如果有重复的发票行号,则更改重复以下的顺序。因此,在示例案例中,您重复了 1,因此它对应于 4(最后一个发票行号 3 之后的那个)
  • 是否所有重复的行都取到最后行号+1的值?还是需要为每个重复的行号增加值?
  • 这听起来并不容易。为什么不简单地按顺序重新编号所有行? 很容易。
  • 大家好,感谢您的 cmets,它们可以带有任何数值,主要目标是不要在输出中重复。

标签: xml xslt xslt-2.0


【解决方案1】:

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

为了查找重复项,我回退到使用 Muenchian Grouping,这通常用于 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>

【讨论】:

  • 最后一次编辑,除了结果枚举中的空白之外,这是一个很好的算术答案。请注意,在 XSLT 2.0 中,不丢失节点身份的重复数据删除是通过 xsl:for-each-groupxsl:sequence 指令完成的。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-29
    • 2013-12-06
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-15
    • 1970-01-01
    相关资源
    最近更新 更多