плоский XML для вложения с использованием XSLT

Тратил слишком много времени на это.."f$%" Надеюсь, вы можете поделиться своим опытом. У меня есть исходный плоский XML, поступающий из внешней базы данных в следующей структуре:

 <?xml version="1.0" encoding="utf-8"?>
    <ns:MT_ActualCosts xmlns:ns="http://percite:scmaster/actual_costs">
        <row>
            <EVENT_ID>106</EVENT_ID>
            <LINE_NUMBER>0</LINE_NUMBER>
            <INVOICE_NUMBER>9999</INVOICE_NUMBER>
            <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
            <CURRENCY_CODE>USD</CURRENCY_CODE>
            <TOTAL_AMOUNT_NET>0001</TOTAL_AMOUNT_NET>
            <RO_NUMBER>102808</RO_NUMBER>
        </row>
        <row>
            <EVENT_ID>106</EVENT_ID>
            <LINE_NUMBER>1</LINE_NUMBER>
            <INVOICE_NUMBER>24444</INVOICE_NUMBER>
            <PLANT>0003</PLANT>
            <ALLOCATION_AMOUNT>122</ALLOCATION_AMOUNT>
        </row>
        <row>
            <EVENT_ID>109</EVENT_ID>
            <LINE_NUMBER>0</LINE_NUMBER>
            <INVOICE_NUMBER>24458</INVOICE_NUMBER>
            <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
            <CURRENCY_CODE>USD</CURRENCY_CODE>
            <TOTAL_AMOUNT_NET>0011</TOTAL_AMOUNT_NET>
            <RO_NUMBER>102813</RO_NUMBER>
        </row>
        <row>
            <EVENT_ID>109</EVENT_ID>
            <LINE_NUMBER>1</LINE_NUMBER>
            <INVOICE_NUMBER>24458</INVOICE_NUMBER>
            <PLANT>0003</PLANT>
            <ALLOCATION_AMOUNT>11.1</ALLOCATION_AMOUNT>
        </row>
        <row>
            <EVENT_ID>108</EVENT_ID>
            <LINE_NUMBER>0</LINE_NUMBER>
            <INVOICE_NUMBER>24535</INVOICE_NUMBER>
            <INVOICE_DATE>2015-02-19 00:00:00.0</INVOICE_DATE>
            <CURRENCY_CODE>USD</CURRENCY_CODE>
            <TOTAL_AMOUNT_NET>11</TOTAL_AMOUNT_NET>
            <RO_NUMBER>102811</RO_NUMBER>
        </row>
        <row>
            <EVENT_ID>108</EVENT_ID>
            <LINE_NUMBER>1</LINE_NUMBER>
            <INVOICE_NUMBER>24535</INVOICE_NUMBER>
            <PLANT>0002</PLANT>
            <ALLOCATION_AMOUNT>11</ALLOCATION_AMOUNT>
        </row>
        <row>
            <EVENT_ID>171</EVENT_ID>
            <LINE_NUMBER>0</LINE_NUMBER>
            <INVOICE_NUMBER>24645</INVOICE_NUMBER>
            <INVOICE_DATE>2015-02-28 00:00:00.0</INVOICE_DATE>
            <CURRENCY_CODE>USD</CURRENCY_CODE>
            <TOTAL_AMOUNT_NET>999999</TOTAL_AMOUNT_NET>
            <RO_NUMBER>103063</RO_NUMBER>
        </row>
        <row>
            <EVENT_ID>171</EVENT_ID>
            <LINE_NUMBER>1</LINE_NUMBER>
            <INVOICE_NUMBER>24645</INVOICE_NUMBER>
            <PLANT>0001</PLANT>
            <ALLOCATION_AMOUNT>11.47</ALLOCATION_AMOUNT>
        </row>
        <row>
            <EVENT_ID>171</EVENT_ID>
            <LINE_NUMBER>2</LINE_NUMBER>
            <INVOICE_NUMBER>24645</INVOICE_NUMBER>
            <PLANT>0001</PLANT>
            <ALLOCATION_AMOUNT>11.53</ALLOCATION_AMOUNT>
        </row>
    </ns:MT_ActualCosts>


My requested Target Structure should be something like this:

[![enter image description here][1]][1]

I need to group under the Header segments RecordLine Segments of the same `EVENT_ID`.

Currently my XSLT can't create the needed structure.
this is my XSLT:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ns0="http://percite:scmaster/actual_costs">
    <xsl:output method="xml" indent="yes"/>

    <!-- xsi:noNamespaceSchemaLocation="\\palnt03\palramnet-redirect$\IL-Users\My-Documents\nimrod_g\SAP\Projects\ScMaster\Finance\ActualCosts\development\xsd"> -->
    <xsl:template match="/">
    <ns0:MT_ActualCostPreNormalized>
    <xsl:for-each select= "ns0:MT_ActualCosts/row">
        <xsl:if test="LINE_NUMBER=0">
            <Header>
            <EVENT_ID><xsl:value-of select="EVENT_ID"></xsl:value-of>
            </EVENT_ID>
            <LINE_NUMBER>
            <xsl:value-of select="LINE_NUMBER"/>
            </LINE_NUMBER>
            <INVOICE_NUMBER>
                    <xsl:value-of select="INVOICE_NUMBER"/>
                    </INVOICE_NUMBER>
                    <CURRENCY_CODE>
                    <xsl:value-of select="CURRENCY_CODE"/>
                    </CURRENCY_CODE>
                    <TOTAL_AMOUNT_NET>
                    <xsl:value-of select="TOTAL_AMOUNT_NET"/>
                    </TOTAL_AMOUNT_NET>
                    <RO_NUMBER>
                    <xsl:value-of select="RO_NUMBER"/>
                    </RO_NUMBER>
                    <PLANT>
                    <xsl:value-of select="PLANT"/>
                    </PLANT>
                    <ALLOCATION_AMOUNT>
                    <xsl:value-of select="ALLOCATION_AMOUNT"/>
                    </ALLOCATION_AMOUNT>
            </Header>
        </xsl:if>
                <xsl:if test="LINE_NUMBER!=0">
                        <RecordLine>
                        <EVENT_ID><xsl:value-of select="EVENT_ID"></xsl:value-of>
                        </EVENT_ID>
                        <LINE_NUMBER>
                        <xsl:value-of select="LINE_NUMBER"/>
                        </LINE_NUMBER>
                        <INVOICE_NUMBER>
                        <xsl:value-of select="INVOICE_NUMBER"/>
                        </INVOICE_NUMBER>
                        <CURRENCY_CODE>
                        <xsl:value-of select="CURRENCY_CODE"/>
                        </CURRENCY_CODE>
                        <TOTAL_AMOUNT_NET>
                        <xsl:value-of select="TOTAL_AMOUNT_NET"/>
                        </TOTAL_AMOUNT_NET>
                        <RO_NUMBER>
                        <xsl:value-of select="RO_NUMBER"/>
                        </RO_NUMBER>
                        <PLANT>
                        <xsl:value-of select="PLANT"/>
                        </PLANT>
                        <ALLOCATION_AMOUNT>
                        <xsl:value-of select="ALLOCATION_AMOUNT"/>
                        </ALLOCATION_AMOUNT>
                        </RecordLine>
                </xsl:if>
        </xsl:for-each>
    </ns0:MT_ActualCostPreNormalized>
    </xsl:template>
    </xsl:stylesheet> 


  [1]: http://i.stack.imgur.com/yklFq.jpg

После реализации большей части кода, предоставленного Дэниелом Хейли, кажется, что решение очень близко. Следуя просьбе Мартина, я добавляю сюда текущий используемый XSLT + текущий XML-результат. Я попытаюсь уточнить свои правила для XSLT-программы: 1. Каждая запись из исходного XML должна быть проанализирована следующим образом: если LINE_NUMBER = 0, то это запись заголовка и она должна создавать сегмент**. сегмент )** - если LINE_NUMBER > 0 и его EVENT_ID равен строке, созданной сегментом, то это RecordLine в контексте того же сегмента и должен открывать сегмент под тем же . результат xml:

  1. Первые 4 группы Headers-RecordLines выходят nicley (EventID от 100 до 104). Но на EVENT_ID 105 что-то пошло не так. видно, что строка с LINE_NUMBER=0 создала сегмент заголовка, но также дважды добавила данные в строку записи. это неправильный результат.
  2. с этого момента все записи путаются. В настоящее время я не могу сказать, почему это произошло. Я добавил часть исходного XML (как изображение) из нашей тестовой базы данных. Я добавил часть XML-результата (в виде изображения). Надеюсь, что эти примечания прояснят проблему.

Исходная часть XML с событием 105

Часть XML результата с событием 105


person Nimrod.G    schedule 10.01.2016    source источник


Ответы (2)


Поскольку вы используете XSLT 2.0, вы можете использовать xsl:for-each-group (см. группировку XSLT 2.0) группировать по EVENT_ID.

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

Пример...

Ввод XML

<ns:MT_ActualCosts xmlns:ns="http://percite:scmaster/actual_costs">
    <row>
        <EVENT_ID>106</EVENT_ID>
        <LINE_NUMBER>0</LINE_NUMBER>
        <INVOICE_NUMBER>9999</INVOICE_NUMBER>
        <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
        <CURRENCY_CODE>USD</CURRENCY_CODE>
        <TOTAL_AMOUNT_NET>0001</TOTAL_AMOUNT_NET>
        <RO_NUMBER>102808</RO_NUMBER>
    </row>
    <row>
        <EVENT_ID>106</EVENT_ID>
        <LINE_NUMBER>1</LINE_NUMBER>
        <INVOICE_NUMBER>24444</INVOICE_NUMBER>
        <PLANT>0003</PLANT>
        <ALLOCATION_AMOUNT>122</ALLOCATION_AMOUNT>
    </row>
    <row>
        <EVENT_ID>109</EVENT_ID>
        <LINE_NUMBER>0</LINE_NUMBER>
        <INVOICE_NUMBER>24458</INVOICE_NUMBER>
        <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
        <CURRENCY_CODE>USD</CURRENCY_CODE>
        <TOTAL_AMOUNT_NET>0011</TOTAL_AMOUNT_NET>
        <RO_NUMBER>102813</RO_NUMBER>
    </row>
    <row>
        <EVENT_ID>109</EVENT_ID>
        <LINE_NUMBER>1</LINE_NUMBER>
        <INVOICE_NUMBER>24458</INVOICE_NUMBER>
        <PLANT>0003</PLANT>
        <ALLOCATION_AMOUNT>11.1</ALLOCATION_AMOUNT>
    </row>
    <row>
        <EVENT_ID>108</EVENT_ID>
        <LINE_NUMBER>0</LINE_NUMBER>
        <INVOICE_NUMBER>24535</INVOICE_NUMBER>
        <INVOICE_DATE>2015-02-19 00:00:00.0</INVOICE_DATE>
        <CURRENCY_CODE>USD</CURRENCY_CODE>
        <TOTAL_AMOUNT_NET>11</TOTAL_AMOUNT_NET>
        <RO_NUMBER>102811</RO_NUMBER>
    </row>
    <row>
        <EVENT_ID>108</EVENT_ID>
        <LINE_NUMBER>1</LINE_NUMBER>
        <INVOICE_NUMBER>24535</INVOICE_NUMBER>
        <PLANT>0002</PLANT>
        <ALLOCATION_AMOUNT>11</ALLOCATION_AMOUNT>
    </row>
    <row>
        <EVENT_ID>171</EVENT_ID>
        <LINE_NUMBER>0</LINE_NUMBER>
        <INVOICE_NUMBER>24645</INVOICE_NUMBER>
        <INVOICE_DATE>2015-02-28 00:00:00.0</INVOICE_DATE>
        <CURRENCY_CODE>USD</CURRENCY_CODE>
        <TOTAL_AMOUNT_NET>999999</TOTAL_AMOUNT_NET>
        <RO_NUMBER>103063</RO_NUMBER>
    </row>
    <row>
        <EVENT_ID>171</EVENT_ID>
        <LINE_NUMBER>1</LINE_NUMBER>
        <INVOICE_NUMBER>24645</INVOICE_NUMBER>
        <PLANT>0001</PLANT>
        <ALLOCATION_AMOUNT>11.47</ALLOCATION_AMOUNT>
    </row>
    <row>
        <EVENT_ID>171</EVENT_ID>
        <LINE_NUMBER>2</LINE_NUMBER>
        <INVOICE_NUMBER>24645</INVOICE_NUMBER>
        <PLANT>0001</PLANT>
        <ALLOCATION_AMOUNT>11.53</ALLOCATION_AMOUNT>
    </row>
</ns:MT_ActualCosts>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:ns="http://percite:scmaster/actual_costs">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!--Identity transform.-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <ns:MT_ActualCostPreNormalized>
      <!--Group rows by EVENT_ID.-->
      <xsl:for-each-group select="row" group-by="EVENT_ID">
        <!--Optional sort by EVENT_ID.-->
        <xsl:sort select="current-grouping-key()"/>
        <Header>
          <!--Apply templates to the children of row's with a LINE_NUMBER of 0 
          followed by row's that have a LINE_NUMBER that isn't equal to 0.-->
          <xsl:apply-templates select="current-group()[LINE_NUMBER=0]/*,
            current-group()[not(LINE_NUMBER=0)]">
            <!--Optional sort by LINE_NUMBER.-->
            <xsl:sort select="LINE_NUMBER"/>
          </xsl:apply-templates>
        </Header>
      </xsl:for-each-group>
    </ns:MT_ActualCostPreNormalized>
  </xsl:template>

  <!--Because of the apply-templates, the only row elements matched 
  should be row's with a LINE_NUMBER > 0.-->
  <xsl:template match="row">
    <RecordLine>
      <xsl:apply-templates/>
    </RecordLine>          
  </xsl:template>

</xsl:stylesheet>

Вывод XML

<ns:MT_ActualCostPreNormalized xmlns:ns="http://percite:scmaster/actual_costs">
   <Header>
      <EVENT_ID>106</EVENT_ID>
      <LINE_NUMBER>0</LINE_NUMBER>
      <INVOICE_NUMBER>9999</INVOICE_NUMBER>
      <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
      <CURRENCY_CODE>USD</CURRENCY_CODE>
      <TOTAL_AMOUNT_NET>0001</TOTAL_AMOUNT_NET>
      <RO_NUMBER>102808</RO_NUMBER>
      <RecordLine>
         <EVENT_ID>106</EVENT_ID>
         <LINE_NUMBER>1</LINE_NUMBER>
         <INVOICE_NUMBER>24444</INVOICE_NUMBER>
         <PLANT>0003</PLANT>
         <ALLOCATION_AMOUNT>122</ALLOCATION_AMOUNT>
      </RecordLine>
   </Header>
   <Header>
      <EVENT_ID>108</EVENT_ID>
      <LINE_NUMBER>0</LINE_NUMBER>
      <INVOICE_NUMBER>24535</INVOICE_NUMBER>
      <INVOICE_DATE>2015-02-19 00:00:00.0</INVOICE_DATE>
      <CURRENCY_CODE>USD</CURRENCY_CODE>
      <TOTAL_AMOUNT_NET>11</TOTAL_AMOUNT_NET>
      <RO_NUMBER>102811</RO_NUMBER>
      <RecordLine>
         <EVENT_ID>108</EVENT_ID>
         <LINE_NUMBER>1</LINE_NUMBER>
         <INVOICE_NUMBER>24535</INVOICE_NUMBER>
         <PLANT>0002</PLANT>
         <ALLOCATION_AMOUNT>11</ALLOCATION_AMOUNT>
      </RecordLine>
   </Header>
   <Header>
      <EVENT_ID>109</EVENT_ID>
      <LINE_NUMBER>0</LINE_NUMBER>
      <INVOICE_NUMBER>24458</INVOICE_NUMBER>
      <INVOICE_DATE>2015-02-09 00:00:00.0</INVOICE_DATE>
      <CURRENCY_CODE>USD</CURRENCY_CODE>
      <TOTAL_AMOUNT_NET>0011</TOTAL_AMOUNT_NET>
      <RO_NUMBER>102813</RO_NUMBER>
      <RecordLine>
         <EVENT_ID>109</EVENT_ID>
         <LINE_NUMBER>1</LINE_NUMBER>
         <INVOICE_NUMBER>24458</INVOICE_NUMBER>
         <PLANT>0003</PLANT>
         <ALLOCATION_AMOUNT>11.1</ALLOCATION_AMOUNT>
      </RecordLine>
   </Header>
   <Header>
      <EVENT_ID>171</EVENT_ID>
      <LINE_NUMBER>0</LINE_NUMBER>
      <INVOICE_NUMBER>24645</INVOICE_NUMBER>
      <INVOICE_DATE>2015-02-28 00:00:00.0</INVOICE_DATE>
      <CURRENCY_CODE>USD</CURRENCY_CODE>
      <TOTAL_AMOUNT_NET>999999</TOTAL_AMOUNT_NET>
      <RO_NUMBER>103063</RO_NUMBER>
      <RecordLine>
         <EVENT_ID>171</EVENT_ID>
         <LINE_NUMBER>1</LINE_NUMBER>
         <INVOICE_NUMBER>24645</INVOICE_NUMBER>
         <PLANT>0001</PLANT>
         <ALLOCATION_AMOUNT>11.47</ALLOCATION_AMOUNT>
      </RecordLine>
      <RecordLine>
         <EVENT_ID>171</EVENT_ID>
         <LINE_NUMBER>2</LINE_NUMBER>
         <INVOICE_NUMBER>24645</INVOICE_NUMBER>
         <PLANT>0001</PLANT>
         <ALLOCATION_AMOUNT>11.53</ALLOCATION_AMOUNT>
      </RecordLine>
   </Header>
</ns:MT_ActualCostPreNormalized>

Рабочий пример

person Daniel Haley    schedule 10.01.2016
comment
Даниил, спасибо за ваше решение. Похоже, где-то внизу результата данные по какой-то причине начинают шифроваться. посмотрите на EVENT_ID = 105. - person Nimrod.G; 11.01.2016

Поскольку вы пишете, что хотите сгруппировать элементы, я думаю, в качестве отправной точки вы можете использовать for-each-group вместо for-each:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:ns0="http://percite:scmaster/actual_costs">

    <xsl:output indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy copy-namespaces="no">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/*">
        <ns0:MT_ActualCostPreNormalized>
          <xsl:for-each-group select="row" group-by="EVENT_ID">
              <Header>
                  <xsl:apply-templates select="EVENT_ID, LINE_NUMBER, current-group()"/>
              </Header>
          </xsl:for-each-group>
        </ns0:MT_ActualCostPreNormalized>
    </xsl:template>

    <xsl:template match="row">
        <RecordLine>
            <xsl:apply-templates/>
        </RecordLine>
    </xsl:template>
</xsl:transform>

Вы можете перечислить дополнительные элементы, которые вы хотите иметь ниже Header в apply-templates.

person Martin Honnen    schedule 10.01.2016
comment
Привет, Мартин, спасибо за твой код. Как я могу удалить строки с LINE_NUMBER=0, поскольку они представляют сегменты заголовков, которые я не хочу включать в свои строки записей. (для обеспечения целостности данных) - person Nimrod.G; 11.01.2016
comment
Если вы вообще не хотите их группировать, измените <xsl:for-each-group select="row" group-by="EVENT_ID"> на <xsl:for-each-group select="row[not(LINE_NUMBER = 0)]" group-by="EVENT_ID">. Если вы хотите сгруппировать их по EVENT_ID, но не хотите включать их в элемент Header как RecordLine, измените <xsl:apply-templates select="EVENT_ID, LINE_NUMBER, current-group()"/> на <xsl:apply-templates select="EVENT_ID, LINE_NUMBER, current-group()[not(LINE_NUMBER = 0)]"/>. - person Martin Honnen; 11.01.2016
comment
Привет, я считаю, что причиной дублирования является ‹xsl:template match=row› ‹RecordLine› ‹xsl:apply-templates/› ‹/RecordLine› ‹/xsl:template› он дублирует LINE_NUMBER 0, который представляет заголовки в Рекордлайн тоже. - person Nimrod.G; 11.01.2016
comment
В этом случае вы можете изменить внутренний <xsl:apply-templates/> на <xsl:apply-templates select="* except LINE_NUMBER"/> или <xsl:apply-templates select="* except LINE_NUMBER[. = 0]"/>. Если у вас все еще есть проблемы, рассмотрите возможность отредактировать свой вопрос и показать нам результат, который вы хотите, в качестве образца кода, я боюсь, что снимка экрана недостаточно, чтобы сказать нам, какие элементы вы хотите включить, а какие нет. - person Martin Honnen; 11.01.2016
comment
Мартин, я изо всех сил старался (из-за ограничений Fourm), чтобы добавить больше данных о моем случае. Надеюсь, это поможет... - person Nimrod.G; 12.01.2016