XSLT, как объединить на основе элемента - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть ниже входной XML:

<Fees>
<user>
    <value>userA</value>
</user>
<feeList>
    <userFee>
        <owner>
            <Id>owner1</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner1</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner2</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner3</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
</feeList>
<user>
    <value>userB</value>
</user>    
<feeList>
    <userFee>
        <owner>
            <Id>owner1</Id>
        </owner>
        <Amount>
            <sum>120</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner2</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner3</Id>
        </owner>
        <Amount>
            <sum>180</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner3</Id>
        </owner>
        <Amount>
            <sum>100</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner4</Id>
        </owner>
        <Amount>
            <sum>75</sum>
        </Amount>
    </userFee>
    <userFee>
        <owner>
            <Id>owner4</Id>
        </owner>
        <Amount>
            <sum>25</sum>
        </Amount>
    </userFee>
</feeList>

В feeList есть 4 userFee элементов. Два из них принадлежат одному владельцу "owner1", и их необходимо объединить, так как 1 и 2 из них принадлежат разным владельцам. Мне нужен следующий вывод:

user: userA    
Total sum: 400
count: 3 (basically no. of unique owner id's for the user fee
    owner and amount: owner1, 200 (Note there are 2 owner1 elements, and they need to be merged into one row with their sum)   
    owner and amount: owner2, 100
    owner and amount: owner3, 100

Пока у меня есть следующий XSLT:
(... Я все еще борюсь с группой и объединяю их.)

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

  <xsl:template match="/">
    <xsl:for-each select="Fees">
   user: <xsl:value-of select="user/value"/>
      <!-- how to get count of unique userFee by owner ID -->
   Count:<xsl:value-of select="count(feeList/userFee)"/>
   Total Sum:<xsl:value-of select="sum(feeList/userFee/amount/sum)"/>
      <xsl:for-each select="feeList/userFee">
         <!-- how to group same owner into one and sum there amount -->
     owner and amount: <xsl:value-of select="owner/Id"/>, <xsl:value-of select="amount/sum"/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

К сожалению, ограничивается XSLT 1.0. Спасибо

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Я имею в виду, я хочу, чтобы количество было 3. Счет 1 на каждого владельца. Также я я сталкиваюсь с проблемой, когда у меня есть больше чем один <feeList>. в Пример я привел только один, но на самом деле может быть много. <feeList> по одному на пользователя, и у меня несколько пользователей файл. Я думаю, проблема в том, что <xsl:key name="overall" match="userFee" use="owner/Id" /> просматривает весь файл. Могу я сделать этот ключ просто посмотреть на каждый элемент <feeList>?

Исходя из этого комментария к другому ответу, мне кажется, что вам нужно использовать составной ключ, используя как владельца / ID, так и пользователя / значение.

Было бы намного легче сказать наверняка, если бы вы обновили свой вопрос примером, который содержит более одного user и соответствующий feeList.

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

Полный пример ...

Ввод XML (обновлено, чтобы иметь более одного user и feeList)

<Fees>
    <user>
        <value>userA</value>
    </user>
    <feeList>
        <userFee>
            <owner>
                <Id>owner1</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner1</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner2</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner3</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
    </feeList>
    <user>
        <value>userB</value>
    </user>    
    <feeList>
        <userFee>
            <owner>
                <Id>owner1</Id>
            </owner>
            <Amount>
                <sum>120</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner2</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner3</Id>
            </owner>
            <Amount>
                <sum>180</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner3</Id>
            </owner>
            <Amount>
                <sum>100</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner4</Id>
            </owner>
            <Amount>
                <sum>75</sum>
            </Amount>
        </userFee>
        <userFee>
            <owner>
                <Id>owner4</Id>
            </owner>
            <Amount>
                <sum>25</sum>
            </Amount>
        </userFee>
    </feeList>
</Fees>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="user_fees" match="userFee" use="concat(../preceding-sibling::user[1]/value,'~',owner/Id)"/>

  <xsl:template match="/*">
    <xsl:for-each select="feeList">
      <xsl:variable name="totalOwners">
        <xsl:for-each select="userFee[count(.|key('user_fees',concat(../preceding-sibling::user[1]/value,'~',owner/Id))[1])=1]">
          <xsl:text>#</xsl:text>
        </xsl:for-each>
      </xsl:variable>
      <xsl:if test="position() > 1"><xsl:text>&#xA;</xsl:text></xsl:if>
      <xsl:value-of select="concat('User: ',preceding-sibling::user[1]/value,'&#xA;')"/>
      <xsl:value-of select="concat('Total Sum: ',sum(userFee/Amount/sum),'&#xA;')"/>
      <xsl:value-of select="concat('Count: ', string-length($totalOwners), '&#xA;')"/>
      <xsl:for-each 
        select="userFee[count(.|key('user_fees',concat(../preceding-sibling::user[1]/value,'~',owner/Id))[1])=1]">
        <xsl:value-of 
          select="concat('&#x9;owner and amount: ',
          owner/Id,
          ', ',
          sum(key('user_fees',concat(../preceding-sibling::user[1]/value,'~',owner/Id))/Amount/sum),'&#xA;')"/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

выход

User: userA
Total Sum: 400
Count: 3
    owner and amount: owner1, 200
    owner and amount: owner2, 100
    owner and amount: owner3, 100

User: userB
Total Sum: 600
Count: 4
    owner and amount: owner1, 120
    owner and amount: owner2, 100
    owner and amount: owner3, 280
    owner and amount: owner4, 100

Fiddle: http://xsltfiddle.liberty -development.net / nc4NzQ8

0 голосов
/ 30 апреля 2018

Это, как упоминал Томалак, задача для мюнхенской группировки . Под этим тегом вы найдете много примеров того, как его использовать. В начале это может быть немного сложно использовать, и из-за этого XSLT-2.0 представил намного более простой xsl:for-each-group для такого рода задач.

Однако, есть одно решение, использующее этот метод:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" omit-xml-declaration="yes" />
  <xsl:key name="overall" match="userFee" use="owner/Id" />   <!-- key required for Muenchian method -->

  <xsl:template match="/Fees">
    User: <xsl:value-of select="user/value"/>
    <!-- iterate over unique owners - Muenchian method -->
    <xsl:for-each select="feeList/userFee[generate-id() = generate-id(key('overall',owner/Id)[1])]">
      Owner:<xsl:value-of select="owner/Id"/>
      <!-- how to get count of unique userFee by owner ID -->
      Count:<xsl:value-of select="count(key('overall',owner/Id))"/>
      <!-- how to sum the amount of one owner -->
      Total Sum:<xsl:value-of select="sum(key('overall',owner/Id)/Amount/sum)"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
    Total count: <xsl:value-of select="count(feeList/userFee)" />
  </xsl:template>

</xsl:stylesheet>

Вывод:

User: userA
Owner:owner1
Count:2
Total Sum:200

Owner:owner2
Count:1
Total Sum:100

Owner:owner3
Count:1
Total Sum:100

Total count: 4
...