2-мерная группировка и суммирование в xslt

Мой ввод xml похож на

<Reports>
  <Report>  
    <ReportHeader>
      <Name>ABC</Name>
      <ReportNo>123</ReportNo>
    </ReportHeader>
    <ReportLine>
      <ReportNo>123</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>10</LineAmount>
    <ReportLine>
    <ReportLine>
      <ReportNo>123</ReportNo>
      <LineGroup>PQR</LineGroup>
      <LineAmount>20</LineAmount>
    <ReportLine>
    <ReportLine>
    <ReportNo>123</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>30</LineAmount>
    <ReportLine>
  </Report>
  <Report>
    <ReportHeader>
      <Name>DEF</Name>
      <ReportNo>456</ReportNo>
    </ReportHeader>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>IJK</LineGroup>
      <LineAmount>40</LineAmount>
    <ReportLine>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>50</LineAmount>
    <ReportLine>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>IJK</LineGroup>
      <LineAmount>60</LineAmount>
    <ReportLine>
  </Report>
</Reports>

Мой вывод xml похож на

<NewReport>
  <Header>
    <Name>ABC</Name>
    <HeaderNo>456</HeaderNo>
  </Header>
  <Line>
    <LineGroup>XYZ</LineGroup>
    <Amount>40</Amount>
  </Line>
  <Line>
    <LineGroup>PQR</LineGroup>
    <Amount>20</Amount>
  </Line>
</NewReport>
<NewReport>
  <Header>
    <Name>DEF</Name>
    <HeaderNo>456</HeaderNo>
  </Header>
  <Line>
    <LineGroup>IJK</LineGroup>
    <Amount>100</Amount>
  </Line>
  <Line>
    <LineGroup>XYZ</LineGroup>
    <Amount>50</Amount>
  </Line>
</NewReport>

XSL, который я использую,

<xsl:key name="KLinesByGroup" match="/Reports/Report/ReportLine" use="LineGroup"/>
<xsl:key name="KLinesByReportNo" match="/Reports/Report/ReportLine" use="ReportNo"/>
<xsl:template match="/">
  <xsl:for-each select="/ns2:Reports/ns2:Report">
     <xsl:variable name="HeaderReportNo"><xsl:value-of select="ReportHeader/ReportNo"/></xsl:variable>
     <Header>
       <Name><xsl:value-of select="ReportHeader/Name"/></Name>
       <HeaderNo><xsl:value-of select="ReportHeader/ReportNo"/></HeaderNo>
       <xsl:apply-templates select="key('KLinesByReportNo', $HeaderReportNo)[1]" mode="reportno-mode"/>
     </Header>
  </xsl:for-each>
</xsl:template>

<xsl:template match="ReportLine" mode="reportno-mode">
  <xsl:apply-templates select="key('KLinesByReportNo', ReportNo)[generate-id() = generate-id(key('KLinesByGroup', LineGroup)[1])]" mode="group-mode"/>
</xsl:template>

<xsl:template match="ReportLine" mode="group-mode">
  <Line>
    <xsl:value-of select="sum(key('KLinesByGroup', LineGroup)/LineAmount)"/>
  </Line>
</xsl:template>

Но результат не тот, что я ожидаю. На выходе я получаю добавление всех сумм на уровне группы или на уровне строки, но не на уровне строки и группы. Может ли кто-нибудь помочь.

Спасибо


person user1619873    schedule 04.09.2012    source источник


Ответы (1)


Это преобразование:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kLine" match="ReportLine"
  use="concat(../ReportHeader/Name, '+',
              ../ReportHeader/ReportNo, '+',
              ReportNo, '+',
              LineGroup
              )"/>

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

 <xsl:template match=
    "ReportLine
      [not(generate-id()
          =
           generate-id(key('kLine',
                           concat(../ReportHeader/Name, '+',
                                  ../ReportHeader/ReportNo, '+',
                                  ReportNo, '+',
                                   LineGroup
                                  )
                          )
                          [1])
           )
      ]"/>

  <xsl:template match="LineAmount">
   <Amount>
     <xsl:value-of select=
     "sum(key('kLine',
              concat(../../ReportHeader/Name, '+',
                     ../../ReportHeader/ReportNo, '+',
                     ../ReportNo, '+',
                     ../LineGroup
                     )
             )
              /LineAmount
         )"/>
   </Amount>
  </xsl:template>

  <xsl:template match="ReportLine/ReportNo"/>

  <xsl:template match="/*"><xsl:apply-templates/></xsl:template>
  <xsl:template match="Report">
   <NewReport><xsl:apply-templates/></NewReport>
  </xsl:template>

  <xsl:template match="ReportHeader">
   <Header><xsl:apply-templates/></Header>
  </xsl:template>

  <xsl:template match="ReportNo">
   <HeaderNo><xsl:apply-templates/></HeaderNo>
  </xsl:template>

  <xsl:template match="ReportLine">
   <Line><xsl:apply-templates/></Line>
  </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML (исправленному из-за нескольких некорректных тегов):

<Reports>
  <Report>
    <ReportHeader>
      <Name>ABC</Name>
      <ReportNo>123</ReportNo>
    </ReportHeader>
    <ReportLine>
      <ReportNo>123</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>10</LineAmount>
    </ReportLine>
    <ReportLine>
      <ReportNo>123</ReportNo>
      <LineGroup>PQR</LineGroup>
      <LineAmount>20</LineAmount>
    </ReportLine>
    <ReportLine>
    <ReportNo>123</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>30</LineAmount>
    </ReportLine>
  </Report>
  <Report>
    <ReportHeader>
      <Name>DEF</Name>
      <ReportNo>456</ReportNo>
    </ReportHeader>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>IJK</LineGroup>
      <LineAmount>40</LineAmount>
    </ReportLine>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>XYZ</LineGroup>
      <LineAmount>50</LineAmount>
    </ReportLine>
    <ReportLine>
      <ReportNo>456</ReportNo>
      <LineGroup>IJK</LineGroup>
      <LineAmount>60</LineAmount>
    </ReportLine>
  </Report>
</Reports>

выдает желаемый правильный результат:

<NewReport>
   <Header>
      <Name>ABC</Name>
      <HeaderNo>123</HeaderNo>
   </Header>
   <Line>
      <LineGroup>XYZ</LineGroup>
      <Amount>40</Amount>
   </Line>
   <Line>
      <LineGroup>PQR</LineGroup>
      <Amount>20</Amount>
   </Line>
</NewReport>
<NewReport>
   <Header>
      <Name>DEF</Name>
      <HeaderNo>456</HeaderNo>
   </Header>
   <Line>
      <LineGroup>IJK</LineGroup>
      <Amount>100</Amount>
   </Line>
   <Line>
      <LineGroup>XYZ</LineGroup>
      <Amount>50</Amount>
   </Line>
</NewReport>

Пояснение:

Надлежащее использование метода мюнхенской группировки с составными ключами и правила идентификации.

person Dimitre Novatchev    schedule 05.09.2012
comment
Идеальный!!!! Мне пришлось немного подправить его для фактического требования ...... но спасибо за код. - person user1619873; 05.09.2012