XSLT 1.0使用同名的多个元素进行分组

时间:2012-10-22 23:15:39

标签: xslt grouping

我的XML看起来像 -

<resultset>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2A:level3A</Hierarchy>
                <Hierarchy>level1B:level2B:level3B</Hierarchy>
            </ITEM>
        </content>
    </hit>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning1</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2A:level3A</Hierarchy>
            </ITEM>
        </content>
    </hit>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning2</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2B:level3C</Hierarchy>
            </ITEM>
        </content>
    </hit>
</resultset>

请注意,有多个层次结构元素是level1:level2:level3的串联字符串 我希望将其变成这样的东西 -

<TREE>
<LEVELS>
<LEVEL1 name="level1A">
 <LEVEL2 name="level2A">
   <LEVEL3 name="level3A">
      <ITEM Name="Office Cleaning"/>
      <ITEM Name="Office Cleaning1"/>
   </LEVEL3>
 </LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
 <LEVEL2 name="level2B">
   <LEVEL3 name="level3B">
        <ITEM Name="Office Cleaning"/>
   </LEVEL3>
 </LEVEL2>
</LEVEL1>
<LEVEL1 name="level1A">
 <LEVEL2 name="level2B">
   <LEVEL3 name="level3C">
      <ITEM Name="Office Cleaning2"/>
    </LEVEL3>
 </LEVEL2>
</LEVEL1>
</LEVELS>
</TREE>

基本上每个项目都有多个与之关联的层次结构。我需要将它们组合在一起。

我只有 -

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:autn="http://schemas.autonomy.com/aci/">

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:key name="HIERARCHYLEVELS" match="resultset/hit/content/ITEM" use="HIERARCHY" />
<xsl:template match="/">
<TREE>

    <xsl:for-each select="resultset/hit/content/ITEM[generate-id()=generate-id(key('HIERARCHYLEVELS', HIERARCHY)[1])]">
        <xsl:for-each select="HIERARCHY">
        <xsl:variable name="level" select="HIERARCHY"/>
        <HIERARCHY name="{$level}" >

            <xsl:variable name="name" select="TITLE"/>

            <ITEM name="{$name}"/>

        </HIERARCHY>
        </xsl:for-each>
    </xsl:for-each>


</TREE>
</xsl:template>

</xsl:stylesheet>

但问题是我只得到第一个匹配的层次结构标记。对于例如我没看到“Office cleaning1”。 我该怎么做才能确保考虑所有层次结构元素?我仍然需要将它分成不同的级别。

2 个答案:

答案 0 :(得分: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="kItemByHier" match="ITEM" use="Hierarchy"/>
 <xsl:key name="kHierByVal" match="Hierarchy" use="."/>

 <xsl:template match="/*">
     <xsl:apply-templates select=
     "*/*/*/Hierarchy[generate-id()=generate-id(key('kHierByVal',.)[1])]"/>
 </xsl:template>

  <xsl:template match="Hierarchy">
    <xsl:call-template name="makeTree">
      <xsl:with-param name="pHier" select="string()"/>
      <xsl:with-param name="pItems" select="key('kItemByHier', .)"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="makeTree">
    <xsl:param name="pHier"/>
    <xsl:param name="pDepth" select="1"/>
    <xsl:param name="pItems" select="/.."/>

    <xsl:choose>
      <xsl:when test="not($pHier)">
        <xsl:for-each select="$pItems">
          <ITEM name="{TITLE}"/>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="LEVEL{$pDepth}">
          <xsl:attribute name="name">
            <xsl:value-of select="substring-before(concat($pHier,':'), ':')"/>
          </xsl:attribute>

          <xsl:call-template name="makeTree">
            <xsl:with-param name="pHier" 
                            select="substring-after($pHier,':')"/>
            <xsl:with-param name="pDepth" select="$pDepth+1"/>
            <xsl:with-param name="pItems" select="$pItems"/>
          </xsl:call-template>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<resultset>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2A:level3A</Hierarchy>
                <Hierarchy>level1B:level2B:level3B</Hierarchy>
            </ITEM>
        </content>
    </hit>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning1</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2A:level3A</Hierarchy>
            </ITEM>
        </content>
    </hit>
    <hit>
        <content>
            <ITEM>
                <TITLE>Office Cleaning2</TITLE>
                <DESCRIPTION>blah blah blah</DESCRIPTION>
                <Hierarchy>level1A:level2B:level3C</Hierarchy>
            </ITEM>
        </content>
    </hit>
</resultset>

生成想要的正确结果

<LEVEL1 name="level1A">
   <LEVEL2 name="level2A">
      <LEVEL3 name="level3A">
         <ITEM name="Office Cleaning"/>
         <ITEM name="Office Cleaning1"/>
      </LEVEL3>
   </LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
   <LEVEL2 name="level2B">
      <LEVEL3 name="level3B">
         <ITEM name="Office Cleaning"/>
      </LEVEL3>
   </LEVEL2>
</LEVEL1>
<LEVEL1 name="level1A">
   <LEVEL2 name="level2B">
      <LEVEL3 name="level3C">
         <ITEM name="Office Cleaning2"/>
      </LEVEL3>
   </LEVEL2>
</LEVEL1>

答案 1 :(得分:0)

为了兴趣,这是一个解决方案的草案。从输出中可以看出它很接近但不安静,因为它使用不同的分组规则。我仍在尝试了解所需的分组规则。如果我能更好地理解,我会更新。

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common"
  exclude-result-prefixes="xsl exsl">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />

<xsl:variable name="phase-1-output">
  <xsl:apply-templates select="/" mode="phase-1" />
</xsl:variable>

<xsl:variable name="phase-2-output">
  <xsl:apply-templates select="exsl:node-set($phase-1-output)" mode="phase-2" />
</xsl:variable>

<xsl:template match="/">
  <xsl:copy-of select="$phase-2-output" />
</xsl:template>

<!--================ Phase 1 ===============================-->    
 <xsl:template match="/" mode="phase-1">
   <t>
     <xsl:apply-templates select="*/*/*/ITEM/Hierarchy" mode="phase-1" />
   </t>
 </xsl:template>

 <xsl:template match="Hierarchy" mode="phase-1">
   <xsl:call-template name="analyze-hierarchy">
     <xsl:with-param name="levels" select="." />
     <xsl:with-param name="item" select="../TITLE" />
   </xsl:call-template>  
 </xsl:template>

<xsl:template name="analyze-hierarchy"><!-- phase-1 -->
  <xsl:param name="levels" />
  <xsl:param name="item" />
  <xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
  <xsl:variable name="e-level" select="
    translate(
      substring($level,1,string-length($level) - 1),
      'abcdefghijklmnopqrstuvwxyz',
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
  <xsl:choose>
    <xsl:when test="$level">
      <xsl:element name="{$e-level}">
        <xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>  
        <xsl:call-template name="analyze-hierarchy">
         <xsl:with-param name="levels" select="substring-after($levels,':')" />
         <xsl:with-param name="item" select="$item" />
        </xsl:call-template>  
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <ITEM Name="{$item}"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  

<!--================ Phase 2 ===============================-->    
<xsl:key name="kLevel"
   match="*[starts-with(name(),'LEVEL')]"
   use="concat(generate-id(..),'|',@name)" />

 <xsl:template match="/" mode="phase-2">
  <TREE>
    <LEVELS>
      <xsl:variable name="t" select="concat(generate-id(t),'|')" />
      <xsl:apply-templates select="t/LEVEL1[
      generate-id() = generate-id( key('kLevel',concat($t,@name))[1])
      ]" mode="phase-2-head" />
    </LEVELS>  
  </TREE>  
</xsl:template>

<xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2-head">
  <xsl:copy>
    <xsl:copy-of select="@*" />
    <xsl:apply-templates select="key('kLevel',concat(generate-id(..),'|',@name))"  mode="phase-2" />
    <xsl:copy-of select="ITEM" />
   </xsl:copy>
 </xsl:template>   

 <xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2">
      <xsl:variable name="p" select="concat(generate-id(.),'|')" />
      <xsl:apply-templates select="*[starts-with(name(),'LEVEL')][
      generate-id() = generate-id( key('kLevel',concat($p,@name))[1])
      ]" mode="phase-2-head" />
 </xsl:template>   

</xsl:stylesheet>

...带样本输入产生这个(不安静的正确输出)......

<TREE>
  <LEVELS>
    <LEVEL1 name="level1A">
      <LEVEL2 name="level2A">
        <LEVEL3 name="level3A">
          <ITEM Name="Office Cleaning" />
        </LEVEL3>
      </LEVEL2>
      <LEVEL2 name="level2A">
        <LEVEL3 name="level3A">
          <ITEM Name="Office Cleaning1" />
        </LEVEL3>
      </LEVEL2>
      <LEVEL2 name="level2B">
        <LEVEL3 name="level3C">
          <ITEM Name="Office Cleaning2" />
        </LEVEL3>
      </LEVEL2>
    </LEVEL1>
    <LEVEL1 name="level1B">
      <LEVEL2 name="level2B">
        <LEVEL3 name="level3B">
          <ITEM Name="Office Cleaning" />
        </LEVEL3>
      </LEVEL2>
    </LEVEL1>
  </LEVELS>
</TREE>

更新

好的,第2轮。我复制了Dimitre的分组规则,它对Hierarchy元素的内容全有或全无。此解决方案生成样本输入的预期输出。请注意,与Dimitre的<xsl:element name="LEVEL{$pDepth}">方法相比,我从层次结构步骤中派生了LEVEL1样式元素名称。我不确定这是否正确。

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

<xsl:key name="kLevel" match="Hierarchy" use="." />

<xsl:template match="/">
  <TREE>
    <LEVELS>
     <xsl:apply-templates select="*/*/*/ITEM/Hierarchy[
        generate-id() = generate-id( key('kLevel',.)[1])
      ]" mode="group" />
    </LEVELS>
   </TREE>
</xsl:template>

 <xsl:template match="Hierarchy" mode="group">
   <xsl:call-template name="analyze-hierarchy">
     <xsl:with-param name="key" select="." />
     <xsl:with-param name="levels" select="." />
   </xsl:call-template>  
 </xsl:template>

<xsl:template name="analyze-hierarchy">
  <xsl:param name="key" />
  <xsl:param name="levels" />
  <xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
  <xsl:variable name="e-level" select="
    translate(
      substring($level,1,string-length($level) - 1),
      'abcdefghijklmnopqrstuvwxyz',
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
  <xsl:choose>
    <xsl:when test="$level">
      <xsl:element name="{$e-level}">
        <xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>  
        <xsl:call-template name="analyze-hierarchy">
         <xsl:with-param name="key" select="$key" />
         <xsl:with-param name="levels" select="substring-after($levels,':')" />
        </xsl:call-template>  
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="key('kLevel',$key)" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  

<xsl:template match="Hierarchy">
  <ITEM Name="{../TITLE}" />
</xsl:template>

</xsl:stylesheet>