2010-05-04 2 views
14

Для каждого узла «агентства» мне нужно найти элементы «stmt», которые имеют одинаковые значения ключа1, key2, key3 и выводят только один узел «stmt» с «comm» и «предварительные» значения суммируются вместе. Для любых элементов «stmt» внутри этого «агентства», которые не соответствуют каким-либо другим элементам «stmt» на основе key1, key2 и key3, мне нужно вывести их как есть. Поэтому после трансформации первый узел «агентства» будет иметь только два «stmt» узла (один подведенный), а второй «агентский» узел будет передан так, как это происходит потому, что ключи не совпадают. Решения XSLT 1.0 или 2.0 в порядке ... хотя моя таблица стилей в настоящее время 1.0. Обратите внимание, что узлы агентства могут иметь любое количество элементов «stmt», которые имеют соответствующие ключи, которые необходимо сгруппировать и суммировать, и любое число, которое этого не делает.Как группировать и суммировать значения в XSLT

<statement> 
<agency> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>15.000</key3> 
     <comm>75.00</comm> 
     <prem>100.00</prem> 
    </stmt> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>15.000</key3> 
     <comm>25.00</comm> 
     <prem>200.00</prem> 
    </stmt> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>17.50</key3> 
     <comm>25.00</comm> 
     <prem>100.00</prem> 
    </stmt> 
</agency> 
<agency> 
    <stmt> 
     <key1>5678</key1> 
     <key2>DEF</key2> 
     <key3>15.000</key3> 
     <comm>10.00</comm> 
     <prem>20.00</prem> 
    </stmt> 
    <stmt> 
     <key1>5678</key1> 
     <key2>DEF</key2> 
     <key3>17.000</key3> 
     <comm>15.00</comm> 
     <prem>12.00</prem> 
    </stmt> 
</agency> 

+0

Хороший вопрос (+1). См. Мой ответ для полного решения XSLT 1.0. –

ответ

10

И XSLT 2.0 решения:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
exclude-result-prefixes="xs" 
> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

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

<xsl:template match="agency"> 
    <agency> 
    <xsl:for-each-group select="stmt" group-by= 
    "concat(key1, '+', key2, '+', key3)"> 

    <stmt> 
     <xsl:copy-of select= 
     "current-group()[1]/*[starts-with(name(),'key')]"/> 

     <comm> 
     <xsl:value-of select="sum(current-group()/comm)"/> 
     </comm> 
     <prem> 
     <xsl:value-of select="sum(current-group()/prem)"/> 
     </prem> 
    </stmt> 
    </xsl:for-each-group> 
    </agency> 
</xsl:template> 
</xsl:stylesheet> 
+0

'concat (key1, key2, key3)' в некоторых случаях будет терпеть неудачу, например 'key1 =" 1A "key2 =" B «key3 =« 1.000 »и« key1 = »1« key2 = «AB» key3 = «1.000» '... Я чувствую, что конкатенация строк без интимного знания их содержимого (или ограничения) неверна. – Lucero

+0

@ Lucero: Еще раз спасибо, что нет ничего плохого в concat - это было что-то, что ускользнуло от меня - я чувствую себя таким сонным целый день сегодня, который теперь исправлен. Пожалуйста, дайте мне знать, если исправление вас удовлетворит. Эта коррекция является чем-то типичным в таких решениях. –

+0

@Dimitre, в отличие от другой проблемы 'concat',' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' + "key2 =" 2 "и' key1 = "1" key2 = "+ 2" '.Поэтому я говорю, что вы должны только контактировать, когда знаете, что разделитель никогда не будет частью конкатенированных данных. – Lucero

7

В XSLT 1.0 использовать Muenchian метод для группировки (с соединением ключа).

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

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

<xsl:key name="kStmtByKeys" match="stmt" 
     use="concat(generate-id(..), key1, '+', key2, '+', key3)"/> 

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

<xsl:template match="agency"> 
    <agency> 
    <xsl:for-each select= 
    "stmt[generate-id() 
      = 
      generate-id(key('kStmtByKeys', 
          concat(generate-id(..), key1, '+', key2, '+', key3) 
          )[1] 
         ) 
      ] 
    "> 
     <xsl:variable name="vkeyGroup" select= 
     "key('kStmtByKeys', concat(generate-id(..), key1, '+', key2, '+', key3))"/> 

    <stmt> 
     <xsl:copy-of select="*[starts-with(name(), 'key')]"/> 
     <comm> 
     <xsl:value-of select="sum($vkeyGroup/comm)"/> 
     </comm> 
     <prem> 
     <xsl:value-of select="sum($vkeyGroup/prem)"/> 
     </prem> 
    </stmt> 
    </xsl:for-each> 
    </agency> 
</xsl:template> 
</xsl:stylesheet> 

при нанесении на предоставленном документе XML, производит желаемый результат:

<statement> 
    <agency> 
     <stmt> 
      <key1>1234</key1> 
      <key2>ABC</key2> 
      <key3>15.000</key3> 
      <comm>100</comm> 
      <prem>300</prem> 
     </stmt> 
     <stmt> 
      <key1>1234</key1> 
      <key2>ABC</key2> 
      <key3>17.50</key3> 
      <comm>25</comm> 
      <prem>100</prem> 
     </stmt> 
    </agency> 
    <agency> 
     <stmt> 
      <key1>5678</key1> 
      <key2>DEF</key2> 
      <key3>15.000</key3> 
      <comm>10</comm> 
      <prem>20</prem> 
     </stmt> 
     <stmt> 
      <key1>5678</key1> 
      <key2>DEF</key2> 
      <key3>17.000</key3> 
      <comm>15</comm> 
      <prem>12</prem> 
     </stmt> 
    </agency> 
</statement> 
+0

Если я правильно понял вопрос, вы нарушите решение, когда у другого агентства есть узлы 'stmt' с теми же ключами. Мне кажется, что, поскольку существует множество агентств, муэнтианский метод с глобальным ключом не будет работать. – Lucero

+0

@ Lucero: Хорошее наблюдение, спасибо. Теперь это исправлено, и я все еще использую метод Muenchian с ключевым словом. –

+0

Хм, это способ генерации сложного ключа, гарантирующего получение желаемых результатов во всех ситуациях? если ключ был 'concat ('1', '23')', а другой был 'concat ('12 ',' 3 ')' (вы получаете идею), это может вызвать проблемы в зависимости от входного документа и XSLT-процессора , – Lucero

1
<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> 
    <xsl:output method="xml" indent="yes"/> 

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

    <xsl:template match="stmt"> 
     <xsl:variable name="stmtGroup" select="../stmt[(key1=current()/key1) and (key2=current()/key2) and (key3=current()/key3)]" /> 
     <xsl:if test="generate-id()=generate-id($stmtGroup[1])"> 
      <xsl:copy> 
       <key1> 
        <xsl:value-of select="key1"/> 
       </key1> 
       <key2> 
        <xsl:value-of select="key2"/> 
       </key2> 
       <key3> 
        <xsl:value-of select="key3"/> 
       </key3> 
       <comm> 
        <xsl:value-of select="format-number(sum($stmtGroup/comm), '#.00')"/> 
       </comm> 
       <prem> 
        <xsl:value-of select="format-number(sum($stmtGroup/prem), '#.00')"/> 
       </prem> 
      </xsl:copy> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet>