简体   繁体   English

XSLT 1.0按多个元素分组

[英]XSLT 1.0 grouping by multiple elements

I came through various answers regarding this topic but I couldn't find a solution so far. 关于这个主题,我有各种各样的答案,但到目前为止,我还没有找到解决方案。

I have input XML with the structure like this: 我输入的XML具有以下结构:

<RootNode>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM11</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM21</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM31</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM41</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM21</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM22</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM23</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM24</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABD</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD21</HEADELEMENT1>
    <HEADELEMENT2>HEAD22</HEADELEMENT2>
    <HEADELEMENT3>HEAD23</HEADELEMENT3>
    <HEADELEMENT4>HEAD24</HEADELEMENT4>
    <ITEMELEMENT1>ITEM31</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM32</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM33</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM34</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>002</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD31</HEADELEMENT1>
    <HEADELEMENT2>HEAD32</HEADELEMENT2>
    <HEADELEMENT3>HEAD33</HEADELEMENT3>
    <HEADELEMENT4>HEAD34</HEADELEMENT4>
    <ITEMELEMENT1>ITEM41</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM42</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM43</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM44</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM51</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM52</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM53</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM54</ITEMELEMENT4>
  </record>
</RootNode>

The result of the transformation should look like this: 转换的结果应如下所示:

<ResultXml>
  <record>
    <header>
      <KEYELEMENT1>001</KEYELEMENT1>
      <KEYELEMENT2>ABC</KEYELEMENT2>
      <KEYELEMENT3>EFG</KEYELEMENT3>
      <HEADELEMENT1>HEAD11</HEADELEMENT1>
      <HEADELEMENT2>HEAD12</HEADELEMENT2>
      <HEADELEMENT3>HEAD13</HEADELEMENT3>
      <HEADELEMENT4>HEAD14</HEADELEMENT4>
    </header>
    <item>
      <ITEMELEMENT1>ITEM11</ITEMELEMENT1>
      <ITEMELEMENT2>ITEM21</ITEMELEMENT2>
      <ITEMELEMENT3>ITEM31</ITEMELEMENT3>
      <ITEMELEMENT4>ITEM41</ITEMELEMENT4>       
    </item>     
    <item>
      <ITEMELEMENT1>ITEM21</ITEMELEMENT1>
      <ITEMELEMENT2>ITEM22</ITEMELEMENT2>
      <ITEMELEMENT3>ITEM23</ITEMELEMENT3>
      <ITEMELEMENT4>ITEM24</ITEMELEMENT4>       
    </item>     
    <item>
      <ITEMELEMENT1>ITEM51</ITEMELEMENT1>
      <ITEMELEMENT2>ITEM52</ITEMELEMENT2>
      <ITEMELEMENT3>ITEM53</ITEMELEMENT3>
      <ITEMELEMENT4>ITEM54</ITEMELEMENT4>       
    </item>
  </record>
  <record>
    <header>
      <KEYELEMENT1>001</KEYELEMENT1>
      <KEYELEMENT2>ABD</KEYELEMENT2>
      <KEYELEMENT3>EFG</KEYELEMENT3>
      <HEADELEMENT1>HEAD21</HEADELEMENT1>
      <HEADELEMENT2>HEAD22</HEADELEMENT2>
      <HEADELEMENT3>HEAD23</HEADELEMENT3>
      <HEADELEMENT4>HEAD24</HEADELEMENT4>
    </header>
    <item>
      <ITEMELEMENT1>ITEM31</ITEMELEMENT1>
      <ITEMELEMENT2>ITEM32</ITEMELEMENT2>
      <ITEMELEMENT3>ITEM33</ITEMELEMENT3>
      <ITEMELEMENT4>ITEM34</ITEMELEMENT4>       
    </item>
  </record>
  <record>
    <header>
      <KEYELEMENT1>002</KEYELEMENT1>
      <KEYELEMENT2>ABC</KEYELEMENT2>
      <KEYELEMENT3>EFG</KEYELEMENT3>
      <HEADELEMENT1>HEAD31</HEADELEMENT1>
      <HEADELEMENT2>HEAD32</HEADELEMENT2>
      <HEADELEMENT3>HEAD33</HEADELEMENT3>
      <HEADELEMENT4>HEAD34</HEADELEMENT4>
    </header>
    <item>
      <ITEMELEMENT1>ITEM41</ITEMELEMENT1>
      <ITEMELEMENT2>ITEM42</ITEMELEMENT2>
      <ITEMELEMENT3>ITEM43</ITEMELEMENT3>
      <ITEMELEMENT4>ITEM44</ITEMELEMENT4>       
    </item>
  </record>   
</ResultXml>

For each distinct values in KEYELEMENT1, KEYELEMENT2, and KEYELEMENT3 I have to create one record in the result. 对于KEYELEMENT1,KEYELEMENT2和KEYELEMENT3中的每个不同值,我必须在结果中创建一个记录。 Other header fields are the same and are transformed to header element with the key fields. 其他标头字段相同,并与键字段一起转换为标头元素。 Items should be mapped under the record with the same keys. 项目应使用相同的键映射到记录下。

I tried the Muenchian method with something like this: 我用以下方法尝试了Muenchian方法:

    <xsl:key name="keyfields" match="record" use="concat(KEYELEMENT1, '|', KEYELEMENT2, '|', KEYELEMENT3)"/>

<xsl:template match="/">
    <ResultXml>
        <xsl:apply-templates select="record[generate-id() = generate-id(key('keyfields',concat(KEYELEMENT1, '|', KEYELEMENT2, '|', KEYELEMENT3))[1])]" mode="header"/>
    </ResultXml>
</xsl:template>

<xsl:template match="record" mode="header">
    <record>
        <header>
            <KEYELEMENT1><xsl:value-of select="KEYELEMENT1"/></KEYELEMENT1>
            <KEYELEMENT2><xsl:value-of select="KEYELEMENT2"/></KEYELEMENT2>
            <KEYELEMENT3><xsl:value-of select="KEYELEMENT3"/></KEYELEMENT3>
            <HEADELEMENT1><xsl:value-of select="HEADELEMENT1"/></HEADELEMENT1>
            <HEADELEMENT2><xsl:value-of select="HEADELEMENT2"/></HEADELEMENT2>
            <HEADELEMENT3><xsl:value-of select="HEADELEMENT3"/></HEADELEMENT3>
            <HEADELEMENT4><xsl:value-of select="HEADELEMENT4"/></HEADELEMENT4>          
        </header>
    </record>       
</xsl:template>

But I am not able to produce even header records. 但是我什至不能产生头记录。 Any help would be appreciated. 任何帮助,将不胜感激。

If you change your first template match from / to /* (or RootNode ) you'll get your groups correctly. 如果将第一个模板匹配项从/更改为/* (或RootNode ),则可以正确获取组。

After that it's just a matter of grouping the items into sets of 4. One way to do that is by using mod in a predicate. 之后,只需将项目分组为4组即可。一种方法是在谓词中使用mod

Here's an example. 这是一个例子。 I use count() instead of generate-id() in my Muenchian grouping, but it can be done either way. 我在Muenchian分组中使用count()而不是generate-id() ,但是可以通过任何一种方式完成。

Example... 例...

XML Input XML输入

<RootNode>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM11</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM21</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM31</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM41</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM21</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM22</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM23</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM24</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABD</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD21</HEADELEMENT1>
    <HEADELEMENT2>HEAD22</HEADELEMENT2>
    <HEADELEMENT3>HEAD23</HEADELEMENT3>
    <HEADELEMENT4>HEAD24</HEADELEMENT4>
    <ITEMELEMENT1>ITEM31</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM32</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM33</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM34</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>002</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD31</HEADELEMENT1>
    <HEADELEMENT2>HEAD32</HEADELEMENT2>
    <HEADELEMENT3>HEAD33</HEADELEMENT3>
    <HEADELEMENT4>HEAD34</HEADELEMENT4>
    <ITEMELEMENT1>ITEM41</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM42</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM43</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM44</ITEMELEMENT4>
  </record>
  <record>
    <KEYELEMENT1>001</KEYELEMENT1>
    <KEYELEMENT2>ABC</KEYELEMENT2>
    <KEYELEMENT3>EFG</KEYELEMENT3>
    <HEADELEMENT1>HEAD11</HEADELEMENT1>
    <HEADELEMENT2>HEAD12</HEADELEMENT2>
    <HEADELEMENT3>HEAD13</HEADELEMENT3>
    <HEADELEMENT4>HEAD14</HEADELEMENT4>
    <ITEMELEMENT1>ITEM51</ITEMELEMENT1>
    <ITEMELEMENT2>ITEM52</ITEMELEMENT2>
    <ITEMELEMENT3>ITEM53</ITEMELEMENT3>
    <ITEMELEMENT4>ITEM54</ITEMELEMENT4>
  </record>
</RootNode>

XSLT 1.0 XSLT 1.0

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

  <xsl:key name="keyfields" match="record"
    use="concat(KEYELEMENT1,'|',KEYELEMENT2,'|',KEYELEMENT3)"/>

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

  <xsl:template match="/*">
    <ResultXml>
      <xsl:for-each
        select="record[count(.|key('keyfields',concat(KEYELEMENT1,'|',KEYELEMENT2,'|',KEYELEMENT3))[1])=1]">
        <record>
          <header>
            <xsl:apply-templates
              select="*[starts-with(local-name(), 'KEYELEMENT') or starts-with(local-name(), 'HEADELEMENT')]"/>
          </header>
          <xsl:for-each select="key('keyfields',concat(KEYELEMENT1,'|',KEYELEMENT2,'|',KEYELEMENT3))/*[starts-with(local-name(),'ITEMELEMENT')][position() mod 4 = 1]">
            <item>
              <xsl:apply-templates 
                select=".|following-sibling::*[starts-with(local-name(),'ITEMELEMENT')][position() >= 1 and 4 > position()]"/>
            </item>
          </xsl:for-each>
        </record>
      </xsl:for-each>
    </ResultXml>
  </xsl:template>

</xsl:stylesheet>

Output 产量

<ResultXml>
   <record>
      <header>
         <KEYELEMENT1>001</KEYELEMENT1>
         <KEYELEMENT2>ABC</KEYELEMENT2>
         <KEYELEMENT3>EFG</KEYELEMENT3>
         <HEADELEMENT1>HEAD11</HEADELEMENT1>
         <HEADELEMENT2>HEAD12</HEADELEMENT2>
         <HEADELEMENT3>HEAD13</HEADELEMENT3>
         <HEADELEMENT4>HEAD14</HEADELEMENT4>
      </header>
      <item>
         <ITEMELEMENT1>ITEM11</ITEMELEMENT1>
         <ITEMELEMENT2>ITEM21</ITEMELEMENT2>
         <ITEMELEMENT3>ITEM31</ITEMELEMENT3>
         <ITEMELEMENT4>ITEM41</ITEMELEMENT4>
      </item>
      <item>
         <ITEMELEMENT1>ITEM21</ITEMELEMENT1>
         <ITEMELEMENT2>ITEM22</ITEMELEMENT2>
         <ITEMELEMENT3>ITEM23</ITEMELEMENT3>
         <ITEMELEMENT4>ITEM24</ITEMELEMENT4>
      </item>
      <item>
         <ITEMELEMENT1>ITEM51</ITEMELEMENT1>
         <ITEMELEMENT2>ITEM52</ITEMELEMENT2>
         <ITEMELEMENT3>ITEM53</ITEMELEMENT3>
         <ITEMELEMENT4>ITEM54</ITEMELEMENT4>
      </item>
   </record>
   <record>
      <header>
         <KEYELEMENT1>001</KEYELEMENT1>
         <KEYELEMENT2>ABD</KEYELEMENT2>
         <KEYELEMENT3>EFG</KEYELEMENT3>
         <HEADELEMENT1>HEAD21</HEADELEMENT1>
         <HEADELEMENT2>HEAD22</HEADELEMENT2>
         <HEADELEMENT3>HEAD23</HEADELEMENT3>
         <HEADELEMENT4>HEAD24</HEADELEMENT4>
      </header>
      <item>
         <ITEMELEMENT1>ITEM31</ITEMELEMENT1>
         <ITEMELEMENT2>ITEM32</ITEMELEMENT2>
         <ITEMELEMENT3>ITEM33</ITEMELEMENT3>
         <ITEMELEMENT4>ITEM34</ITEMELEMENT4>
      </item>
   </record>
   <record>
      <header>
         <KEYELEMENT1>002</KEYELEMENT1>
         <KEYELEMENT2>ABC</KEYELEMENT2>
         <KEYELEMENT3>EFG</KEYELEMENT3>
         <HEADELEMENT1>HEAD31</HEADELEMENT1>
         <HEADELEMENT2>HEAD32</HEADELEMENT2>
         <HEADELEMENT3>HEAD33</HEADELEMENT3>
         <HEADELEMENT4>HEAD34</HEADELEMENT4>
      </header>
      <item>
         <ITEMELEMENT1>ITEM41</ITEMELEMENT1>
         <ITEMELEMENT2>ITEM42</ITEMELEMENT2>
         <ITEMELEMENT3>ITEM43</ITEMELEMENT3>
         <ITEMELEMENT4>ITEM44</ITEMELEMENT4>
      </item>
   </record>
</ResultXml>

Fiddle: http://xsltfiddle.liberty-development.net/3Nqn5Yi 小提琴: http : //xsltfiddle.liberty-development.net/3Nqn5Yi

Muenchiann grouping in XSLT 1.0 requires a bit of ordered approach and careful usage of a number of grouping idioms . XSLT 1.0中的 Muenchiann分组需要一些有序的方法并仔细使用许多分组习惯用法

We must start from creation of a key , to group records, in this case on KEYELEMENT1 / ...2 / ...3. 我们必须从创建键开始 ,对记录进行分组,在这种情况下,在KEYELEMENT1 / / 2/2/3上进行。

Then the main template (matching RootNode ) applies "group" template to the first record from each group. 然后,主模板(匹配RootNode )将“组”模板应用于每个组的第一条记录。

The "group" template for record : “本集团”模板record

  • prints opening record tag, 打印开始record标签,
  • prints head element filled with KEY... and HEAD... source elements, 打印head元件填充有KEY ...... HEAD源元件,
  • calls "normal" template to all members of the current group, 对当前组的所有成员调用“正常”模板,
  • and finally, prints closing record tag. 最后,打印结束record标签。

The "normal" template for record prints item element filled with ITEM... source elements. “正常”模板record打印item充满项...源元素的元素。

And the last thing you need is the identity template . 最后,您需要的是身份模板

So the whole script looks like below: 因此,整个脚本如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:key name="recs" match="record"
    use="concat(KEYELEMENT1, '|', KEYELEMENT2, '|', KEYELEMENT3)"/>

  <xsl:template match="RootNode">
    <ResultXml>
      <!-- Apply "group" template to the first record in group -->
      <xsl:apply-templates select="record[generate-id() = generate-id(
        key('recs', concat(KEYELEMENT1, '|', KEYELEMENT2, '|', KEYELEMENT3))
        [1])]" mode="group"/>
    </ResultXml>
  </xsl:template>

  <!-- "Group" template for record -->
  <xsl:template match="record" mode="group">
    <record>
      <head>
        <xsl:copy-of select="*[starts-with(name(), 'KEY') or starts-with(name(), 'HEAD')]"/>
      </head>
      <!-- Apply "normal" template to all members of the current group -->
      <xsl:apply-templates select="key('recs',
        concat(KEYELEMENT1, '|', KEYELEMENT2, '|', KEYELEMENT3))"/>
    </record>
  </xsl:template>

  <!-- "Normal" template for record -->
  <xsl:template match="record">
    <item>
      <xsl:copy-of select="*[starts-with(name(), 'ITEM')]"/>
    </item>
  </xsl:template>

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

For a working example see http://xsltfiddle.liberty-development.net/6qM2e2k 有关工作示例,请参见http://xsltfiddle.liberty-development.net/6qM2e2k

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM