首页 > 解决方案 > XSLT 2.0 通过分组创建计数器

问题描述

我正在尝试为分组元素创建一个计数器。

资源:

<?xml version="1.0" encoding="UTF-8"?>
<root_com>
    <root_por-out>
        <is_globalprocessid>1370284</is_globalprocessid>
        <is_processid>1370284</is_processid>
        <partneridentcode>123456</partneridentcode>
        <por-out>
            <por_number>320060916</por_number>
            <order_pos>10</order_pos>
            <order_pos_partner>10</order_pos_partner>
        </por-out>
        <por-out>
            <por_number>320060916</por_number>
            <order_number_partner>875421</order_number_partner>
            <order_pos>20</order_pos>
            <order_pos_partner>20</order_pos_partner>
        </por-out>
        <por-out>
            <por_number>320060916</por_number>
            <order_pos>30</order_pos>
            <order_pos_partner>10</order_pos_partner>
        </por-out>
                <por-out>
            <por_number>320060916</por_number>
            <order_pos>40</order_pos>
            <order_pos_partner>30</order_pos_partner>
        </por-out>
        <por-out>
            <por_number>320060916</por_number>
            <order_pos>50</order_pos>
            <order_pos_partner>10</order_pos_partner>
        </por-out>
    </root_por-out>
</root_com>

期望的输出:

<Confirmation>
    <Settings>
        <DecimalSymbol>.</DecimalSymbol>
    </Settings>
    <Orders>
        <Order>
            <OrderIdSupplier>320060916</OrderIdSupplier>
            <OrderItems>
                <OrderItem>
                    <LineNumber>10</LineNumber>
                    <ItemSubNo>1</ItemSubNo>
                </OrderItem>
                <OrderItem>
                    <LineNumber>20</LineNumber>
                    <ItemSubNo>1</ItemSubNo>
                </OrderItem>
                <OrderItem>
                    <LineNumber>10</LineNumber>
                    <ItemSubNo>2</ItemSubNo>
                </OrderItem>
                <OrderItem>
                    <LineNumber>30</LineNumber>
                    <ItemSubNo>1</ItemSubNo>
                </OrderItem>
                <OrderItem>
                    <LineNumber>10</LineNumber>
                    <ItemSubNo>3</ItemSubNo>
                </OrderItem>
            </OrderItems>
        </Order>
    </Orders>
</Confirmation>

我目前拥有的代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
    <Settings>
        <DecimalSymbol>.</DecimalSymbol>
    </Settings>
    <Orders>
        <Order>
            <PurchaseNo><xsl:value-of select="por-out[1]/order_number_partner"/></PurchaseNo>
            <SupplierId><xsl:value-of select="partneridentcode"/></SupplierId>
            <OrderIdSupplier><xsl:value-of select="por-out[1]/por_number"/></OrderIdSupplier>
            <OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
                    <DeliveryDate><xsl:value-of select="delivery_date"/></DeliveryDate>
                                <LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
                    <ProductId><xsl:value-of select="article_partner"/></ProductId>
                    <Price><xsl:value-of select="price"/></Price>
                    <PriceFactor>1</PriceFactor>
                    <Quantity><xsl:value-of select="quantity"/></Quantity>
                </OrderItem></xsl:for-each-group></xsl:for-each>
                
            </OrderItems>
        </Order>
    </Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>

代码简化为核心问题:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
    <Settings>
        <DecimalSymbol>.</DecimalSymbol>
    </Settings>
    <Orders>
        <Order>
            <OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
                        <LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
                </OrderItem></xsl:for-each-group></xsl:for-each>
                
            </OrderItems>
        </Order>
    </Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>

重点是:客户发送订单。我们提供回应。由于订购量很大,我们需要在不同的日期交付订购的零件。我们拆分原始位置。在我们的回复中,客户获得了对他原始订单位置(order_pos_partner)的引用,并且客户还需要一个用于拆分头寸数量的计数器(目标:“ItemSubNo”)

我怎样才能做到这一点?

如果输出按位置排序,那很好,但不需要。

谢谢

标签: xsltgroup-bycountsequenceedi

解决方案


如果你使用

  <xsl:template match="root_por-out">
    <Confirmation>
        <Orders>
          <Order>
            <OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
            <xsl:for-each-group select="por-out" group-by="order_pos_partner">
              <xsl:apply-templates select="current-group()"/>
            </xsl:for-each-group>
          </Order>
        </Orders>
    </Confirmation>
  </xsl:template>

  <xsl:template match="por-out">
    <OrderItem>
      <LineNumber>{current-grouping-key()}</LineNumber>
      <ItemSubNumber>{position()}</ItemSubNumber>
    </OrderItem>    
  </xsl:template>

你会得到

        <OrderItem>
           <LineNumber>10</LineNumber>
           <ItemSubNumber>1</ItemSubNumber>
        </OrderItem>
        <OrderItem>
           <LineNumber>10</LineNumber>
           <ItemSubNumber>2</ItemSubNumber>
        </OrderItem>
        <OrderItem>
           <LineNumber>10</LineNumber>
           <ItemSubNumber>3</ItemSubNumber>
        </OrderItem>
        <OrderItem>
           <LineNumber>20</LineNumber>
           <ItemSubNumber>1</ItemSubNumber>
        </OrderItem>
        <OrderItem>
           <LineNumber>30</LineNumber>
           <ItemSubNumber>1</ItemSubNumber>
        </OrderItem> 

所以我认为这有正确的数字,虽然不是你显示的顺序。

要保留原始输入顺序,您可以仅使用分组来存储正确的序列,然后稍后将它们映射:

  <xsl:template match="root_por-out">
    <Confirmation>
        <Orders>
          <Order>
            <OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
            <xsl:variable name="groups" as="map(*)">
              <xsl:map>
                <xsl:for-each-group select="por-out" group-by="order_pos_partner">
                  <xsl:map-entry key="current-grouping-key()" select="current-group() ! generate-id()"/>
                </xsl:for-each-group>                
              </xsl:map>
            </xsl:variable>
            <xsl:apply-templates select="*">
              <xsl:with-param name="groups" select="$groups"/>
            </xsl:apply-templates>
          </Order>
        </Orders>
    </Confirmation>
  </xsl:template>
  
  <xsl:template match="por-out">
    <xsl:param name="groups"/>
    <OrderItem>
      <LineNumber>{order_pos_partner}</LineNumber>
      <ItemSubNumber>{index-of($groups(order_pos_partner), generate-id())}</ItemSubNumber>
    </OrderItem>    
  </xsl:template>

两个示例都使用 XSLT 3,尽管更详细(<LineNumber>{order_pos_partner}</LineNumber>而不是<LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber>XML 数据结构而不是轻量级映射)

  <xsl:key name="group" match="group" use="@key"/>
  
  <xsl:template match="root_por-out">
    <Confirmation>
        <Orders>
          <Order>
            <OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
            <xsl:variable name="groups">
                <xsl:for-each-group select="por-out" group-by="order_pos_partner">
                  <group key="{current-grouping-key()}">
                    <xsl:for-each select="current-group()">
                      <item>
                        <xsl:value-of select="generate-id()"/>
                      </item>
                    </xsl:for-each>
                  </group>
                </xsl:for-each-group>                
            </xsl:variable>
            <xsl:apply-templates select="*">
              <xsl:with-param name="groups" select="$groups"/>
            </xsl:apply-templates>
          </Order>
        </Orders>
    </Confirmation>
  </xsl:template>
  
  <xsl:template match="por-out">
    <xsl:param name="groups"/>
    <OrderItem>
      <LineNumber>
        <xsl:value-of select="order_pos_partner"/>
      </LineNumber>
      <ItemSubNumber>
        <xsl:value-of select="index-of(key('group', order_pos_partner, $groups)/item, generate-id())"/>
      </ItemSubNumber>
    </OrderItem>    
  </xsl:template>

它可以在 XSLT 2 中完成。


推荐阅读