使用xslt构建列表

时间:2009-04-09 16:24:22

标签: xslt

我正在尝试构建一个解析整个xml文档的列表。我需要列出数字名称,然后列出alpha名称。列表看起来应该是这样的。

6
6600 Training
6500 Training

A
Accelerated Training

T
Training

这是xml的片段。

<courses>
    <course>         
        <name>Accelerated Training</name>
    </course>
    <course>        
        <name>6600 Training</name>
    </course>   
         <course>        
        <name>Training</name>
    </course>
    <course>        
        <name>6500 Training</name>
    </course>   

</courses>   

这是我目前正在使用的代码。我在网站上的另一个问题中发现了这一点,并对其进行了一些定制。目前它没有考虑到我需要按编号解析,它也会按字母顺序返回。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

  <xsl:output omit-xml-declaration="yes" indent="yes"/> 
  <xsl:variable name="vLower" select= "'abcdefghijklmnopqrstuvwxyz'"/> 
  <xsl:variable name="vUpper" select= "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> 

  <xsl:key name="kTitleBy1stLetter" match="courses/course"  use="substring(name,1,1)"/>    

  <xsl:template match="/*">      

    <xsl:for-each select="course [generate-id() = generate-id(key('kTitleBy1stLetter', substring(name,1,1)) [1] ) ]">        
      <xsl:variable name="v1st" select="substring(name,1,1)"/>        
      <h2><xsl:value-of select="$v1st"/></h2>        
      <div class="{translate($v1st, $vUpper, $vLower)}-content">
        <ul>
          <xsl:for-each select="key('kTitleBy1stLetter',$v1st)">               
            <li><xsl:value-of select="name"/></li>
          </xsl:for-each>          
        </ul>      
      </div>      
    </xsl:for-each>        
  </xsl:template>
</xsl:stylesheet>

3 个答案:

答案 0 :(得分:7)

基本上,您需要按第一个字母分组,然后按<name>排序。你的Muenchian分组方法已经很好了。

我建议另一种方法更容易:

<xsl:key name="kInitial" match="course" use="substring(name, 1, 1)" />

<xsl:template match="courses">
  <xsl:apply-templates select="course" mode="initial">
    <xsl:sort select="name" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="course" mode="initial">
  <xsl:variable name="initial" select="substring(name, 1, 1)" />
  <xsl:variable name="courses" select="key('kInitial', $initial)" />
  <xsl:if test="generate-id() = generate-id($courses[1])">
    <h2><xsl:value-of select="$initial"/></h2>
    <ul>
      <xsl:apply-templates select="$courses">
        <xsl:sort select="name" />
      </xsl:apply-templates>
    </ul>
  </xsl:if>
</xsl:template>

<xsl:template match="course">
  <li>
    <xsl:value-of select="name"/>
  </li>
</xsl:template>

输出:

<h2>6</h2>
<ul>
  <li>6500 Training</li>
  <li>6600 Training</li>
</ul>
<h2>A</h2>
<ul>
  <li>Accelerated Training</li>
</ul>
<h2>T</h2>
<ul>
  <li>Training</li>
</ul>

编辑:为了易读,我遗漏了第一个字母的上半身。正确的密钥就是这个(你不能在密钥中使用变量,因此是文字字母串):

<xsl:key name="kInitial" match="course" use="
  translate(
    substring(name, 1, 1), 
    'abcdefghijklmnopqrstuvwxyz', 
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  )
" />

第二个模板中的$initial变量当然也是如此,但实际上你可以在这里再次使用变量。

编辑#2:由于排序也区分大小写,因此您可以使用相同的表达式:

<xsl:sort select="translate(substring(name, 1, 1), $vLower, $vUpper)" />

答案 1 :(得分:2)

XSLT 2.0解决方案

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

    <xsl:template match="/*">
      <xsl:for-each-group select="course"
           group-by="upper-case(substring(name,1,1))">
        <xsl:sort select="current-grouping-key()"/>

        <xsl:sequence select=
           "concat('&#xA;', current-grouping-key())"/>

        <xsl:for-each select="current-group()">
          <xsl:sort select="upper-case(name)"/>
          <xsl:sequence select="concat('&#xA;&#x9;', name)"/>
        </xsl:for-each>
      </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

将上述转换应用于最初提供的XML文档

<courses>
    <course>
        <name>Accelerated Training</name>
    </course>
    <course>
        <name>6600 Training</name>
    </course>
    <course>
        <name>Training</name>
    </course>
    <course>
        <name>6500 Training</name>
    </course>
</courses>

生成想要的结果(为简单起见,文本格式 - 生成Html留给读者练习:)

6 
    6500 Training 
    6600 Training 
A 
    Accelerated Training 
T 
    Training

请注意

  1. 使用 <xsl:for-each-group> XSLT 2.0指令

  2. 使用 current-grouping-key() current-group() XSLT 2.0函数。

  3. 使用 upper-case() XPath 2.0功能

答案 2 :(得分:0)

如果你想要任何复杂的数字部分是很棘手的,但根据你理想的输出,你所缺少的只是一个简单的sort你的每个人:

<xsl:sort select="key('kTitleBy1stLetter', substring(name,1,1))" />

警告:我没有声称这是最好或唯一或其他方法,只是这完全停止,并使用你已有的。