Объединение XML-сообщений на основе значения элемента, включая субистемы

Мне нужно объединить узлы XML на основе значения элемента и создать дочерние узлы для некоторых элементов

Пример ввода XML:

<?xml version="1.0" encoding="UTF-8"?>
<orders>
    <Order>
        <OrderNumber>SO0000001</OrderNumber>
        <!--All Orders with same OrderNumber are combines-->
        <OrderLineNumber>000010</OrderLineNumber>
        <!--Product sequence number in order-->
        <OrderDate>20181026090000</OrderDate>
        <CustomerNumber>0000112233</CustomerNumber>
        <CustomerName>Customer name 01</CustomerName>
        <Quantity>1</Quantity>
        <SaleRevenue>20</SaleRevenue>
        <UnitPrice>15</UnitPrice>
        <Currency>USD</Currency>
        <ProductNumber>ZZZ000BBB</ProductNumber>
        <!--Product number in order-->
        <ProductDescription>Product 01</ProductDescription>
    </Order>
    <Order>
        <OrderNumber>SO0000001</OrderNumber> 
        <OrderLineNumber>000020</OrderLineNumber>
        <OrderDate>20181026090000</OrderDate>
        <CustomerNumber>0000112233</CustomerNumber>
        <CustomerName>Customer name 01</CustomerName>
        <Quantity>2</Quantity>
        <SaleRevenue>20</SaleRevenue>
        <UnitPrice>5</UnitPrice>
        <Currency>USD</Currency>
        <ProductNumber>ZZZ000AAA</ProductNumber>
        <ProductDescription>Product 02</ProductDescription>
    </Order>
    <Order>
        <OrderNumber>SO0000999</OrderNumber>
        <OrderLineNumber>000010</OrderLineNumber>
        <OrderDate>20181026090100</OrderDate>
        <CustomerNumber>0000778899</CustomerNumber>
        <CustomerName>Customer name 02</CustomerName>
        <Quantity>1</Quantity>
        <SaleRevenue>60</SaleRevenue>
        <UnitPrice>10</UnitPrice>
        <Currency>USD</Currency>
        <ProductNumber>ABC00111</ProductNumber>
        <ProductDescription>Product 03</ProductDescription>
    </Order>
    <Order>
        <OrderNumber>SO0000999</OrderNumber>
        <OrderLineNumber>000020</OrderLineNumber>
        <OrderDate>20181026090100</OrderDate>
        <CustomerNumber>0000778899</CustomerNumber>
        <CustomerName>Customer name 02</CustomerName>
        <Quantity>2</Quantity>
        <SaleRevenue>60</SaleRevenue>
        <UnitPrice>20</UnitPrice>
        <Currency>USD</Currency>
        <ProductNumber>ABC00222</ProductNumber>
        <ProductDescription>Product 04</ProductDescription>
    </Order>
    <Order>
        <OrderNumber>SO0000999</OrderNumber>
        <OrderLineNumber>0000230</OrderLineNumber>
        <OrderDate>20181026090100</OrderDate>
        <CustomerNumber>0000778899</CustomerNumber>
        <CustomerName>Customer name 02</CustomerName>
        <Quantity>3</Quantity>
        <SaleRevenue>60</SaleRevenue>
        <UnitPrice>30</UnitPrice>
        <Currency>USD</Currency>
        <ProductNumber>ABC00333</ProductNumber>
        <ProductDescription>Product 05</ProductDescription>
    </Order>
</orders>

от этого мне нравится создавать следующие результаты с использованием XSLT 1.0 или 2.0

<?xml version="1.0" encoding="UTF-8"?>
<orders>
    <Order>
        <OrderNumber>SO0000001</OrderNumber>    
        <OrderDate>20181026090000</OrderDate>
        <CustomerNumber>0000112233</CustomerNumber>
        <CustomerName>Customer name 01</CustomerName>
        <SaleRevenue>20</SaleRevenue>
        <products>
            <Product>
                <OrderLineNumber>000010</OrderLineNumber>
                <UnitPrice>15</UnitPrice>
                <Currency>USD</Currency>
                <Quantity>1</Quantity>
                <ProductNumber>ZZZ000BBB</ProductNumber>
                <ProductDescription>Product 01</ProductDescription>
            </Product>
            <Product>
                <OrderLineNumber>000020</OrderLineNumber>
                <Quantity>2</Quantity>
                <UnitPrice>5</UnitPrice>
                <Currency>USD</Currency>
                <ProductNumber>ZZZ000AAA</ProductNumber>
                <ProductDescription>Product 02</ProductDescription>
            </Product>
        </products>
    </Order>
    <Order>
        <OrderNumber>SO0000999</OrderNumber>
        <OrderDate>20181026090100</OrderDate>
        <CustomerNumber>0000778899</CustomerNumber>
        <CustomerName>Customer name 02</CustomerName>
        <products>
            <Product>
                <OrderLineNumber>000010</OrderLineNumber>
                <Quantity>1</Quantity>
                <UnitPrice>10</UnitPrice>
                <Currency>USD</Currency>
                <ProductNumber>ABC00111</ProductNumber>
                <ProductDescription>Product 03</ProductDescription>
            </Product>
            <Product>
                <OrderLineNumber>000020</OrderLineNumber>
                <Quantity>2</Quantity>
                <UnitPrice>20</UnitPrice>
                <Currency>USD</Currency>
                <ProductNumber>ABC00222</ProductNumber>
                <ProductDescription>Product 04</ProductDescription>
            </Product>
            <Product>
                <Quantity>3</Quantity>
                <UnitPrice>30</UnitPrice>
                <Currency>USD</Currency>
                <ProductNumber>ABC00333</ProductNumber>
                <ProductDescription>Product 05</ProductDescription>
            </Product>
        </products>
    </Order>
</orders>

Я думаю, что я близок, но могу получить последнюю работу. Я не могу получить продукты, входящие в состав Ордена, так как я использую для каждой группы (я думаю).

Это мой текущий XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    <xsl:template match="orders">
        <orders>
            <xsl:for-each-group select="Order" group-by="OrderNumber">  
                <Order>
                    <xsl:if test="current-group()[2]">
                        <!--<OrderNumber name="{OrderNumber}"/>--> <!--products node-->
                        <xsl:copy-of select="OrderNumber"/>
                        <!--<xsl:copy-of select="OrderLineNumber"/>--> <!--products node-->
                        <xsl:copy-of select="OrderDate"/>
                        <xsl:copy-of select="CustomerNumber"/>
                        <xsl:copy-of select="CustomerName"/>
                        <!--<xsl:copy-of select="Quantity"/>--> <!--products node-->
                        <xsl:copy-of select="SaleRevenue"/>
                        <!--<xsl:copy-of select="UnitPrice"/>--> <!--products node-->
                        <xsl:copy-of select="Currency"/>
                        <!--<xsl:copy-of select="LineStatus"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ProductNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ModelNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="SerialNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ProductDescription"/>--> <!--products node-->
                    </xsl:if>
                </Order>
            </xsl:for-each-group>
            <xsl:if test="Order">
            <products>
                <xsl:for-each select="Order"> 
                    <product>
                        <xsl:copy-of select="OrderLineNumber"/>
                        <xsl:copy-of select="Quantity"/>
                        <xsl:copy-of select="UnitPrice"/>
                        <xsl:copy-of select="LineStatus"/>
                        <xsl:copy-of select="ProductNumber"/>
                        <xsl:copy-of select="ProductDescription"/>
                    </product>
                </xsl:for-each>
            </products>
            </xsl:if>
        </orders>
    </xsl:template>
</xsl:stylesheet>

Я получаю работу группировки, но вам нужно будет добавить все найденные данные продукта к созданным продуктам дочерних узлов. Я не эксперт XSLT и не уверен, что я на правильном пути.

Всего 2 ответа


Если вы хотите, чтобы продукты были вложены в элемент заказа, вам необходимо переместить соответствующий блок кода в создание элемента <order> . Я также не вижу, что пытается выполнить тест current-group()[2] , но я не думаю, что вам это действительно нужно.

Попробуйте этот XSLT (который включает более короткий способ копирования существующих узлов)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" indent="yes"/>
  <xsl:template match="orders">
    <orders>
      <xsl:for-each-group select="Order" group-by="OrderNumber">  
        <Order>
          <xsl:copy-of select="OrderNumber,OrderDate,CustomerNumber,CustomerName,SaleRevenue"/>
          <products>
            <xsl:for-each select="current-group()"> 
              <product>
                <xsl:copy-of select="OrderLineNumber,UnitPrice,Currency,Quantity,ProductNumber,ProductDescription"/>
              </product>
            </xsl:for-each>
          </products>          
        </Order>
      </xsl:for-each-group>
    </orders>
  </xsl:template>
</xsl:stylesheet>

Переместите products xsl:for-each в группу xsl:for-each-group и добавьте абсолютный путь к элементам Order . Поэтому измените свой XSLT на

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    <xsl:template match="orders">
        <orders>
            <xsl:for-each-group select="Order" group-by="OrderNumber">  
                <Order>
                    <xsl:if test="current-group()[2]">
                        <!--<OrderNumber name="{OrderNumber}"/>--> <!--products node-->
                        <xsl:copy-of select="OrderNumber"/>
                        <!--<xsl:copy-of select="OrderLineNumber"/>--> <!--products node-->
                        <xsl:copy-of select="OrderDate"/>
                        <xsl:copy-of select="CustomerNumber"/>
                        <xsl:copy-of select="CustomerName"/>
                        <!--<xsl:copy-of select="Quantity"/>--> <!--products node-->
                        <xsl:copy-of select="SaleRevenue"/>
                        <!--<xsl:copy-of select="UnitPrice"/>--> <!--products node-->
                        <xsl:copy-of select="Currency"/>
                        <!--<xsl:copy-of select="LineStatus"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ProductNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ModelNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="SerialNumber"/>--> <!--products node-->
                        <!--<xsl:copy-of select="ProductDescription"/>--> <!--products node-->
                        <!-- Moved to here -->
                        <products>
                            <xsl:for-each select="/orders/Order[OrderNumber = current()/OrderNumber]"> 
                                <product>
                                    <xsl:copy-of select="OrderLineNumber"/>
                                    <xsl:copy-of select="Quantity"/>
                                    <xsl:copy-of select="UnitPrice"/>
                                    <xsl:copy-of select="Currency"/>
                                    <xsl:copy-of select="LineStatus"/>
                                    <xsl:copy-of select="ProductNumber"/>
                                    <xsl:copy-of select="ProductDescription"/>
                                </product>
                            </xsl:for-each>
                        </products>
                    </xsl:if>
                </Order>
            </xsl:for-each-group>
            <xsl:if test="Order">
                <!-- Moved for-each from here to above ^^^ -->
            </xsl:if>
        </orders>
    </xsl:template>
</xsl:stylesheet>

Это дает желаемый результат.


Есть идеи?

10000