XSLT2.0: как использовать вложенные для каждой группы для сжатия записей XML

Я пытаюсь использовать вложенные для каждой группы и для каждого с current-group () для сжатия подобных данных в одну запись xml на основе двух ключей. Первый ключ - это ID, а второй - Inv_Link.

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

Спасибо за любую помощь!

Набор данных XML

<Data>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE2</Link>
        <Component_ID>DEBT</Component_ID>
        <Amt>1500</Amt>
    </Record>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE1</Link>
        <Component_ID>EQT</Component_ID>
        <Amt>200</Amt>
    </Record>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE1</Link>
        <Component_ID>CASH</Component_ID>
        <Amt>100</Amt>
    </Record>
    <Record>
        <ID>01_2020</ID>
        <Link>ICE3</Link>
        <Component_ID>CASH</Component_ID>
        <Amt>100</Amt>
    </Record>
</Data>

XSLT, который я использую сейчас:

   <xsl:template match="Data">
        <xsl:for-each-group select="Record" group-by="ID">
        <xsl:for-each select="current-group()">
          <Record>
            <groupkey><xsl:value-of select="current-grouping-key()"/></groupkey>
            <AssetEvent>
                <ID> <xsl:copy> <xsl:value-of select="ID/text()" /> </xsl:copy> </ID>
                <DecompositionSequence>
                    <xsl:for-each-group select="Record" group-by="Link">
                    <groupkey><xsl:value-of select="current-grouping-key()"/></groupkey>
                    <xsl:for-each select="current-group()">
                        <Decompositions>
                            <Link> 
                              <ID><xsl:copy><xsl:value-of select="Link/text()" /> </xsl:copy> <ID> 
                            </Link>
                            <DecompositionDataSequence>
                                <DecompositionData>
                                    <Component>
                                        <ID> <xsl:copy> <xsl:value-of select="Component_ID/text()" /> </xsl:copy> </ID> 
                                    </Component>
                                    <Amt> <xsl:copy> <xsl:value-of select="Amt/text()" /> </xsl:copy> </Amt>
                                </DecompositionData>
                            </DecompositionDataSequence>
                        </Decompositions>   
                    </xsl:for-each>
                    </xsl:for-each-group>
              </DecompositionSequence>
            </AssetEvent>
          </Record>
        </xsl:for-each>
        </xsl:for-each-group>
  </xsl:template>

Текущие результаты, я получаю всего 2 записи, но без внутренней группировки (если я использую // Запись, я получаю все для обеих записей результатов):

    <Record>
      <groupkey>ICE 01_2019</groupkey>
      <AssetEvent>
        <ID>ICE 01_2019</ID>
        <DecompositionSequence />
      </AssetEvent>
    </Record>
    <Record>
      <groupkey>01_2020</groupkey>
      <AssetEvent>
        <ID>01_2020</ID>
        <DecompositionSequence />
      </AssetEvent>
    </Record>


Чего я жду:

    <Record>
      <groupkey>01_2019</groupkey>
      <AssetEvent>
        <ID>01_2019</ID>
        <DecompositionSequence>
          <groupkey>ICE2</groupkey>
          <Decompositions>
            <InvestmentLink>ICE2</InvestmentLink>
            <DecompositionDataSequence>
              <DecompositionData>
                <Component>
                  <ID>DEBT</ID>
                </Component>
                <Amt>150</Amt>
              </DecompositionData>
            </DecompositionDataSequence>
          </Decompositions>
          <groupkey>ICE1</groupkey>
          <Decompositions>
            <InvestmentLink>ICE1</InvestmentLink>
            <DecompositionDataSequence>
              <DecompositionData>
                <Component>
                  <ID>EQT</ID>
                </Component>
                <Amt>150</Amt>
              </DecompositionData>
              <DecompositionData>
                <Component>
                  <ID>CASH</ID>
                </Component>
                <Amt>150</Amt>
              </DecompositionData>
            </DecompositionDataSequence>
          </Decompositions>
        </DecompositionSequence>
      </AssetEvent>
    </Record>
    <Record>
      <groupkey>01_2020</groupkey>
      <AssetEvent>
        <ID>01_2020</ID>
        <DecompositionSequence>
          <groupkey>ICE3</groupkey>
          <Decompositions>
            <InvestmentLink>ICE3</InvestmentLink>
            <DecompositionDataSequence>
              <DecompositionData>
                <Component>
                  <ID>CASH</ID>
                </Component>
                <Amt>100</Amt>
              </DecompositionData>
            </DecompositionDataSequence>
          </Decompositions>
        </DecompositionSequence>
      </AssetEvent>
    </Record>


person soad122    schedule 15.07.2019    source источник
comment
В общем, если вы хотите сгруппировать элементы, чтобы удалить дубликаты или объединить их, вы не должны использовать for-each-group с напрямую вложенным for-each поверх current-group(), так вы будете отображать, например, четыре входа Record с одним и тем же ключом к четырем выходным Record. Поэтому обычно внутри <xsl:for-each-group select="Record" group-by="..."> вы используете <xsl:copy>...</xsl:copy> для слияния, например. четыре ввода Record с одним и тем же ключом группировки для одного результата Record.   -  person Martin Honnen    schedule 15.07.2019
comment
И использование путей, начинающихся с // внутри любого вложенного for-each-group или for-each, также выглядит неправильно, поскольку выполняется поиск по всему документу.   -  person Martin Honnen    schedule 15.07.2019
comment
@MartinHonnen Спасибо за это, помог прояснить некоторые проблемы. Я обновил сообщение о том, где я застрял. У меня проблемы с получением всех дочерних ключей. Думаю, я использую неправильный селектор.   -  person soad122    schedule 15.07.2019
comment
Возможно, вы захотите сократить образец данных до соответствующего материала для вложенной группировки, но затем показать полный образец результата, соответственно, объяснить, как именно вы хотите сгруппировать и сопоставить ввод с выводом. Выполнение xsl:for-each-group select="." кажется бессмысленным, поскольку отдельный элемент, сгруппированный по любому ключу, ничего не дает.   -  person Martin Honnen    schedule 15.07.2019
comment
Как я уже сказал, в целом подход <xsl:for-each-group select="Record" group-by="some-key"><xsl:copy>...<xsl:for-each-group select="current-group()" group-by="some-other-key"><!-- sequence constructor for the inner group --></xsl:for-each-group></xsl:copy></xsl:for-each-group>. Из вашего вопроса неясно, какие ключи группировки вы хотите (текст говорит Record/ID, код имеет concat(ID,'|',Description), выходной образец имеет значение ICE 01_2019|1/1/2019|Jan Report 2019|4, которое, похоже, содержит еще некоторые другие данные), ни какую структуру вы хотите.   -  person Martin Honnen    schedule 15.07.2019
comment
Я удалил все ненужные элементы данных, чтобы упростить отслеживание. Извините, я новичок в xslt, поэтому пришлось немного проб и ошибок выяснить это.   -  person soad122    schedule 15.07.2019
comment
Теперь в тексте говорится, что вы хотите сгруппировать по Inv_Link, но в отредактированном примере только первый Record имеет такой Inv_Link дочерний элемент, а другие Record имеют Link дочерних элементов; Я до сих пор не могу понять, какая у вас структура ввода и как вы хотите сгруппировать / сопоставить ее с каким результатом.   -  person Martin Honnen    schedule 15.07.2019


Ответы (1)


Предполагая, что ваш входной образец

<Data>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE2</Link>
        <Component_ID>DEBT</Component_ID>
        <Amt>1500</Amt>
    </Record>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE1</Link>
        <Component_ID>EQT</Component_ID>
        <Amt>200</Amt>
    </Record>
    <Record>
        <ID>01_2019</ID>
        <Link>ICE1</Link>
        <Component_ID>CASH</Component_ID>
        <Amt>100</Amt>
    </Record>
    <Record>
        <ID>01_2020</ID>
        <Link>ICE3</Link>
        <Component_ID>CASH</Component_ID>
        <Amt>100</Amt>
    </Record>
</Data>

тогда код

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="Data">
    <xsl:for-each-group select="Record" group-by="ID">
        <xsl:copy>
            <groupkey>
                <xsl:value-of select="current-grouping-key()"/>
            </groupkey>
            <AssetEvent>
                <xsl:copy-of select="ID"/>
                <DecompositionSequence>
                  <xsl:for-each-group select="current-group()" group-by="Link">
                      <groupkey>
                          <xsl:value-of select="current-grouping-key()"/>
                      </groupkey>
                      <Decompositions>
                          <InvestmentLink>
                              <xsl:value-of select="current-grouping-key()"/>
                          </InvestmentLink>
                          <DecompositionDataSequence>
                              <xsl:apply-templates select="current-group()"/>
                          </DecompositionDataSequence>
                      </Decompositions>
                  </xsl:for-each-group>
                </DecompositionSequence>
            </AssetEvent>

        </xsl:copy>
      </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="Record">
        <DecompositionData>
            <xsl:apply-templates select="* except (ID, Link)"/>
        </DecompositionData>
    </xsl:template>

    <xsl:template match="Component_ID">
        <Component>
            <ID>
                <xsl:value-of select="."/>
            </ID>
        </Component>
    </xsl:template>

</xsl:stylesheet>

дает результат

<?xml version="1.0" encoding="UTF-8"?>
<Record>
   <groupkey>01_2019</groupkey>
   <AssetEvent>
      <ID>01_2019</ID>
      <DecompositionSequence>
         <groupkey>ICE2</groupkey>
         <Decompositions>
            <InvestmentLink>ICE2</InvestmentLink>
            <DecompositionDataSequence>
               <DecompositionData>
                  <Component>
                     <ID>DEBT</ID>
                  </Component>
                  <Amt>1500</Amt>
               </DecompositionData>
            </DecompositionDataSequence>
         </Decompositions>
         <groupkey>ICE1</groupkey>
         <Decompositions>
            <InvestmentLink>ICE1</InvestmentLink>
            <DecompositionDataSequence>
               <DecompositionData>
                  <Component>
                     <ID>EQT</ID>
                  </Component>
                  <Amt>200</Amt>
               </DecompositionData>
               <DecompositionData>
                  <Component>
                     <ID>CASH</ID>
                  </Component>
                  <Amt>100</Amt>
               </DecompositionData>
            </DecompositionDataSequence>
         </Decompositions>
      </DecompositionSequence>
   </AssetEvent>
</Record>
<Record>
   <groupkey>01_2020</groupkey>
   <AssetEvent>
      <ID>01_2020</ID>
      <DecompositionSequence>
         <groupkey>ICE3</groupkey>
         <Decompositions>
            <InvestmentLink>ICE3</InvestmentLink>
            <DecompositionDataSequence>
               <DecompositionData>
                  <Component>
                     <ID>CASH</ID>
                  </Component>
                  <Amt>100</Amt>
               </DecompositionData>
            </DecompositionDataSequence>
         </Decompositions>
      </DecompositionSequence>
   </AssetEvent>
</Record>

Здесь используется объявление XSLT 3 <xsl:mode on-no-match="shallow-copy"/>, но если вы используете процессор XSLT 2, вы можете заменить его шаблоном преобразования идентичности.

person Martin Honnen    schedule 15.07.2019
comment
Спасибо, это очень помогло, извините за оригинальный беспорядок, который я опубликовал. - person soad122; 16.07.2019