2016-01-10 6 views
0

Проводил слишком много времени на этом. «F $%» Надеюсь, вы сможете поделиться своим опытом. У меня есть источник плоский XML, прибывающий из внешнего БД в следующей структуре:Плоский XML для вставки с использованием XSLT

<?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 

После implemenitng большой части коды, представленной Daniel Хейли кажется, что решение очень близко. Запрос Follwoing Martin Я добавляю здесь текущий XSLT, используемый + текущий XML-результат. Я попытаюсь выполнить мои правила для программы XSLT: 1. Каждая запись из исходного XML должна быть проанализирована следующим образом: , если LINE_NUMBER = 0, то это запись заголовка и должна создать сегмент **. (It shouldnt быть добавленным в сегмент) ** - если LINE_NUMBER> 0, а его EVENT_ID равно строке, созданной сегментом, это RecordLine в контексте того же сегмента и должен открыть сегмент под тем же самым. " Немногие проблемы можно увидеть в результате XML:

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

Source XML part with event 105

Result XML part with event 105

ответ

0

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

Также используйте identity transform для обработки всех элементов, которые не нуждаются в изменении.

Пример ...

XML Input

<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 Output

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

Working Example

+0

Daniel, Спасибо за ваше решение. Похоже, что где-то в результате данные по какой-то причине начинают карабкаться. посмотрите EVENT_ID = 105. –

1

Как вы пишете, что вы хотите, чтобы сгруппировать элементы, которые я предполагаю, что в качестве отправной точки вы можете использовать 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.

+0

Привет Мартин, спасибо за ваш код. Как удалить строки с LINE_NUMBER = 0, поскольку они представляют сегменты заголовка, которые я не хочу включать в свои RecordLines. (для сохранения целостности данных) –

+0

Если вы не хотите группировать их при любом изменении '' to ' '. Если вы хотите сгруппировать их по EVENT_ID, но не хотите включать их в элемент 'Header' как' RecordLine', тогда измените ' 'to' ' –

+0

Привет, я считаю, что причиной дублирования является дублируется LINE_NUMBER "0", представляющий заголовки в RecordLine INTO, а также. –