Group/merge childs of same nodes in xml/xslt

前端 未结 2 744
悲哀的现实
悲哀的现实 2020-12-11 08:24

I am new to XSLT and changing it manually will take a lot of time.


 

        
相关标签:
2条回答
  • 2020-12-11 08:49

    This XSLT 1.0 transformation:

    <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="kGDByIdKey" match="GroupData"
      use="concat(@ID, '+', @Key)"/>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match=
     "GroupData
        [generate-id()
        =
         generate-id(key('kGDByIdKey', concat(@ID, '+', @Key))[1])
         ]">
        <xsl:copy>
          <xsl:apply-templates select=
           "@*|key('kGDByIdKey', concat(@ID, '+', @Key))/node()"/>
        </xsl:copy>
     </xsl:template>
    
      <xsl:template match="GroupData"/>
    </xsl:stylesheet>
    

    when applied on this XML document:

    <t>
        <GroupData ID="xxx" Key="4" Temp="yyy">
            <ItemData ID="zzz" Value="3"/>
        </GroupData>
        <GroupData ID="yyy" Key="4" Temp="yyy">
            <ItemData ID="abc" Value="3"/>
        </GroupData>
        <GroupData ID="zzz" Temp="yyy">
            <ItemData ID="pqr" Value="1982"/>
        </GroupData>
        <GroupData ID="xxx" Key="4" Temp="yyy">
            <ItemData ID="www" Value="1982"/>
        </GroupData>
        <GroupData ID="yyy" Key="4" Temp="yyy">
            <ItemData ID="def" Value="1982"/>
        </GroupData>
        <GroupData ID="zzz" Temp="yyy">
            <ItemData ID="tuv" Value="1982"/>
        </GroupData>
    </t>
    

    produces the wanted, correct result:

    <t>
       <GroupData ID="xxx" Key="4" Temp="yyy">
          <ItemData ID="zzz" Value="3"/>
          <ItemData ID="www" Value="1982"/>
       </GroupData>
       <GroupData ID="yyy" Key="4" Temp="yyy">
          <ItemData ID="abc" Value="3"/>
          <ItemData ID="def" Value="1982"/>
       </GroupData>
       <GroupData ID="zzz" Temp="yyy">
          <ItemData ID="pqr" Value="1982"/>
          <ItemData ID="tuv" Value="1982"/>
       </GroupData>
    </t>
    

    Explanation:

    Proper use of the Muenchian grouping method and the identity rule.

    0 讨论(0)
  • 2020-12-11 09:03

    If it is XSLT 2.0 then you can use a nested <xsl:for-each-group>

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:template match="/">
        <Groups>
          <xsl:for-each-group select="/Groups/GroupData" group-by="@ID">
            <xsl:for-each-group select="current-group()" group-by="if(@Key) then @Key else 'no key'">
              <GroupData>
                <!-- Copy attributes off the *first* GroupData element in the group -->
                <xsl:copy-of select="current-group()[1]/@*"/>
                <!-- Copy ItemData children from *all* GroupData elements in the group -->
                <xsl:copy-of select="current-group()/ItemData" />
              </GroupData>
            </xsl:for-each-group>
          </xsl:for-each-group>
        </Groups>
      </xsl:template>
    </xsl:stylesheet>
    

    (I'm assuming your input file has a root element <Groups> and uses no namespaces).

    If it's XSLT 1.0 then you need to use Muenchian Grouping:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:key name="group-data" match="GroupData" use="concat(@ID, '___', @Key)" />
      <xsl:template match="/">
        <Groups>
          <!--
          Iterate over a node set containing just one GroupData element for
          each combination of ID and Key
          -->
          <xsl:for-each select="/Groups/GroupData[count( . | key('group-data', concat(@ID, '___', @Key))[1]) = 1]">
            <GroupData>
              <!-- Copy attributes from the "prototype" GroupData -->
              <xsl:copy-of select="@*"/>
              <!--
              Copy ItemData children from *all* GroupData elements with matching
              ID/Key
              -->
              <xsl:copy-of select="key('group-data', concat(@ID, '___', @Key))/ItemData" />
            </GroupData>
          </xsl:for-each>
        </Groups>
      </xsl:template>
    </xsl:stylesheet>
    

    Here I'm doing a single grouping pass based on both the ID and Key attributes by creating a synthetic key value of {ID}___{Key}.

    0 讨论(0)
提交回复
热议问题