XSLT如何基于元素进行合并

时间:2018-04-30 19:34:42

标签: xml xslt xslt-1.0

我有以下输入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>

userFee中有4个feeList个元素。其中两个属于同一所有者&#34; owner1&#34;这些需要合并,因为其中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 :(得分:1)

正如Tomalak所说,这是Muenchian Grouping的任务。在此标记下,您将找到许多如何使用它的示例。在开始时使用起来可能有点棘手,因为XSLT-2.0为这类任务引入了更简单的<Body> <!--Button to pull out marker co-ords--> <p><button onclick="document.getElementById('latlnginpara').innerHTML = map.marker.getBounds()">Get map bounds</button></p> <!--This is where the co-ords will sit for now--> <div id="latlnginpara"></div> <!--This plots a slot for the map--> <div id="map" style="width:100%;height:500px;"></div> <!--Now starts the Map fun--> <script> function myMap() { var mapCanvas = document.getElementById("map"); var myCenter=new google.maps.LatLng(51.508742,-0.120850); var mapOptions = { center: myCenter, zoom: 5, zoomControl: true, mapTypeControl: true, scaleControl: true, streetViewControl: true, overviewMapControl: true, rotateControl: true, }; var map = new google.maps.Map(mapCanvas, mapOptions); //1 marker bit var marker; function placeMarker(location) { if ( marker ) { marker.setPosition(location); } else { marker = new google.maps.Marker({ position: location, map: map }); } } google.maps.event.addListener(map, 'click', function(event) { placeMarker(event.latLng); }); } </script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD5WIxyWvYfFU3vY27DM7mc-JClPwPLB0A&callback=myMap"></script> </Body>

但是,这是应用此方法的一种解决方案:

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>

答案 1 :(得分:1)

  

我的意思是,我希望计数为3.每个所有者1个。还有,我   当我有多个<feeList>时,我遇到了问题。在里面   例如我只给了一个,但实际上可以有很多。   <feeList>是每个用户一个,我实际上有多个用户   文件。我认为问题在于<xsl:key name="overall" match="userFee" use="owner/Id" />查看整个文件。我可以吗   使这个键只调查每个<feeList>元素?

根据对其他答案的评论,听起来我需要使用所有者/ ID以及用户/值来使用复合键。

如果您使用包含多个user和相应feeList的示例更新了您的问题,则更容易确定。

此外,要计算每个用户的所有者数量,您可以创建一个为每个所有者输出字符串字符的变量。字符串的长度将是所有者的数量。可能有更好的方法来做到这一点,但现在不会想到它。

完整示例......

XML输入(已更新为多个userfeeList

<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

小提琴:http://xsltfiddle.liberty-development.net/nc4NzQ8