xsl用于迭代并添加

时间:2012-10-23 21:01:03

标签: xml xslt

输入:

<root><element>
  <small>a</small>
  <Large>B</Large>
  <Time>301</Time></element><element>
  <small>a</small>
  <Large>B</Large>
  <Time>322</Time></element><element>
  <small>b</small>
  <Large>A</Large>
  <Time>274</Time></element><element>
  <small>c</small>
  <Large>B</Large>
  <Time>325</Time></element><element>
  <small>b</small>
  <Large>A</Large>
  <Time>301</Time></element></root>

需要编写一个xslt,查看small和Large元素成对的次数,并列出smallnum标记中的计数,并将多次迭代的时间添加到totsmalltime标记。

输出:

<root><element>
  <small>a</small>
  <Large>B</Large>
  <smallnum>2</smallnum>
  <totsmalltime>623</totsmalltime></element><element>
  <small>b</small>
  <Large>A</Large>
  <smallnum>2</smallnum>
  <totsmalltime>575</totsmalltime></element><element>
  <small>c</small>
  <Large>B</Large>
  <smallnum>1</smallnum>
  <totsmalltime>325</totsmalltime></element></root>

3 个答案:

答案 0 :(得分:2)

要在XSLT1.0中执行此操作,您将使用Muenchian Grouping。在您的情况下,您通过前面的第一个元素的组合对时间元素进行分组。这意味着您将首先定义以下键

<xsl:key 
   name="pairs" 
   match="Time" 
   use="concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1])" />

然后,您需要首先出现在组中的时间元素作为其特定键。你可以这样做:

<xsl:apply-templates 
  select="element/Time[
     generate-id()
     = generate-id(
         key(
           'pairs', 
           concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1])
          )[1])]" />

然后,例如,要获取 smallnum 值,即组中所有元素的值,您只需执行此操作,其中 $ key 为定义为concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1])

<xsl:value-of select="count(key('pairs', $key))" />

对于 totsmalltime 元素,只需使用sum而不是count。

这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:key name="pairs" match="Time" use="concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1])"/>

   <xsl:template match="/root">
      <root>
         <xsl:apply-templates select="element/Time[generate-id() = generate-id(key('pairs', concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1]))[1])]"/>
      </root>
   </xsl:template>

   <xsl:template match="Time">
      <xsl:variable name="small" select="preceding-sibling::small[1]"/>
      <xsl:variable name="Large" select="preceding-sibling::Large[1]"/>
      <xsl:variable name="key" select="concat($Large, '|', $small)"/>
      <element>
         <xsl:copy-of select="$small"/>
         <xsl:copy-of select="$Large"/>
         <smallnum>
            <xsl:value-of select="count(key('pairs', $key))"/>
         </smallnum>
         <totsmalltime>
            <xsl:value-of select="sum(key('pairs', $key))"/>
         </totsmalltime>
      </element>
   </xsl:template>
</xsl:stylesheet>

应用于XML时,输出以下内容

<root>
   <element>
      <small>a</small>
      <Large>B</Large>
      <smallnum>2</smallnum>
      <totsmalltime>623</totsmalltime>
   </element>
   <element>
      <small>b</small>
      <Large>A</Large>
      <smallnum>2</smallnum>
      <totsmalltime>575</totsmalltime>
   </element>
   <element>
      <small>c</small>
      <Large>B</Large>
      <smallnum>1</smallnum>
      <totsmalltime>325</totsmalltime>
   </element>
</root>

编辑:在XSLT2.0中,您可以在执行计数时使用 xsl:for-each-group 元素以及 current-group()总和。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/root">
      <root>
         <xsl:for-each-group select="element/Time" group-by="concat(preceding-sibling::Large[1], '|', preceding-sibling::small[1])">
            <element>
               <xsl:copy-of select="preceding-sibling::small[1]"/>
               <xsl:copy-of select="preceding-sibling::Large[1]"/>
               <smallnum>
                  <xsl:value-of select="count(current-group())"/>
               </smallnum>
               <totsmalltime>
                  <xsl:value-of select="sum(current-group())"/>
               </totsmalltime>
            </element>
         </xsl:for-each-group>
      </root>
   </xsl:template>
</xsl:stylesheet>

这也应该输出相同的XML。

答案 1 :(得分:0)

您所追求的技术称为 Muenchian分组,您希望通过elementsmall值的组合对Large元素进行分组:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="smallLarge" match="element"
        use="concat(small, '_', Large)" />

  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="root/element[generate-id() = generate-id(
             key('smallLarge', concat(small, '_', Large)[1])]" />
    </root>
  </xsl:template>

  <xsl:template match="element">
    <element>
      <small><xsl:value-of select="small" /></small>
      <Large><xsl:value-of select="Large" /></Large>
      <smallnum><xsl:value-of select="count(key('smallLarge',
                  concat(small, '_', Large)))" /></smallnum>
      <totsmalltime><xsl:value-of select="sum(key('smallLarge',
                  concat(small, '_', Large))/Time)" /></totsmalltime>
    </element>
  </xsl:template>
</xsl:stylesheet>

有趣的一行是

      <xsl:apply-templates select="root/element[generate-id() = generate-id(
             key('smallLarge', concat(small, '_', Large)[1])]" />

使用generate-id为每个键值仅选择第一个 element元素。因此,element模板将针对每个small / Large组合调用一次,该模板可以使用该键来计算和总结该特定键值的所有时间。

P.S。原始XML示例上的缩进很糟糕,我花了几个读数来注意element元素,起初它看起来像一个smallLarge和{{1}的长序列兄弟姐妹......

答案 2 :(得分:0)

<强>予。 XSLT 1.0解决方案:

这是一个完全“推动式”的简短解决方案:)

<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="kElem" match="element"
  use="concat(small, '+', Large)"/>

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

 <xsl:template match=
 "element[generate-id()=generate-id(key('kElem',concat(small, '+', Large))[1])]">
  <xsl:variable name="vGroup" select="key('kElem',concat(small, '+', Large))"/>

  <element>
   <xsl:apply-templates/>
   <smallnum><xsl:value-of select="count($vGroup)"/></smallnum>
   <totsmalltime><xsl:value-of select="sum($vGroup/Time)"/></totsmalltime>
  </element>
 </xsl:template>
 <xsl:template match="element|Time"/>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<root>
    <element>
        <small>a</small>
        <Large>B</Large>
        <Time>301</Time>
    </element>
    <element>
        <small>a</small>
        <Large>B</Large>
        <Time>322</Time>
    </element>
    <element>
        <small>b</small>
        <Large>A</Large>
        <Time>274</Time>
    </element>
    <element>
        <small>c</small>
        <Large>B</Large>
        <Time>325</Time>
    </element>
    <element>
        <small>b</small>
        <Large>A</Large>
        <Time>301</Time>
    </element>
</root>

产生了想要的正确结果:

<root>
   <element>
      <small>a</small>
      <Large>B</Large>
      <smallnum>2</smallnum>
      <totsmalltime>623</totsmalltime>
   </element>
   <element>
      <small>b</small>
      <Large>A</Large>
      <smallnum>2</smallnum>
      <totsmalltime>575</totsmalltime>
   </element>
   <element>
      <small>c</small>
      <Large>B</Large>
      <smallnum>1</smallnum>
      <totsmalltime>325</totsmalltime>
   </element>
</root>

<强>解释

  1. 正确使用和覆盖 identity rule

  2. 正确使用 Muenchian Grouping Method


  3. <强> II。 XSLT 2.0解决方案:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/*">
      <root>
       <xsl:for-each-group select="*" group-by="concat(small, '+', Large)">
        <element>
         <xsl:sequence select="small, Large"/>
         <smallnum>
           <xsl:sequence select="count(current-group())"/>
         </smallnum>
         <totsmalltime>
          <xsl:sequence select="sum(current-group()/Time)"/>
         </totsmalltime>
        </element>
       </xsl:for-each-group>
      </root>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于同一XML文档(上图)时,会产生相同的正确结果

    <root>
       <element>
          <small>a</small>
          <Large>B</Large>
          <smallnum>2</smallnum>
          <totsmalltime>623</totsmalltime>
       </element>
       <element>
          <small>b</small>
          <Large>A</Large>
          <smallnum>2</smallnum>
          <totsmalltime>575</totsmalltime>
       </element>
       <element>
          <small>c</small>
          <Large>B</Large>
          <smallnum>1</smallnum>
          <totsmalltime>325</totsmalltime>
       </element>
    </root>
    

    <强>解释

    1. 正确使用带有group-by属性的 <xsl:for-each-group>

    2. 正确使用 current-group() 功能。