Xslt 为字母表中的每个字母循环xsl:的更好方法?

Xslt 为字母表中的每个字母循环xsl:的更好方法?,xslt,xslt-1.0,Xslt,Xslt 1.0,我有一个很长的XML文件,我需要从中提取书名和其他信息,然后按字母顺序排序,每个字母都有一个分隔符。我还需要一个部分的项目,不以字母开头,说一个数字或符号。比如: # 1494-精装版,9.99美元 A 《金沙》之后——平装本,24.95美元 北极精神号-精装,65.00美元 B 平装本,18.95美元 我还需要创建一个单独的作者列表,从相同的数据创建,但显示不同类型的信息 我现在是怎么做的 这是简化的,但我基本上有两次相同的代码,一次用于标题,一次用于作者。模板的作者版本使用不同的元素,并对

我有一个很长的XML文件,我需要从中提取书名和其他信息,然后按字母顺序排序,每个字母都有一个分隔符。我还需要一个部分的项目,不以字母开头,说一个数字或符号。比如:

#

1494-精装版,9.99美元

A

《金沙》之后——平装本,24.95美元

北极精神号-精装,65.00美元

B

平装本,18.95美元

我还需要创建一个单独的作者列表,从相同的数据创建,但显示不同类型的信息

我现在是怎么做的 这是简化的,但我基本上有两次相同的代码,一次用于标题,一次用于作者。模板的作者版本使用不同的元素,并对数据执行不同的操作,因此我不能使用相同的模板

<xsl:call-template name="BIP-letter">
    <xsl:with-param name="letter" select="'#'" />
</xsl:call-template>
<xsl:call-template name="BIP-letter">
    <xsl:with-param name="letter" select="'A'" />
</xsl:call-template>
…
<xsl:call-template name="BIP-letter">
    <xsl:with-param name="letter" select="'Z'" />
</xsl:call-template>

<xsl:template name="BIP-letter">
    <xsl:param name="letter" />
    <xsl:choose>
        <xsl:when test="$letter = '#'">
            <xsl:text>#</xsl:text>
            <xsl:for-each select="//Book[
                                  not(substring(Title,1,1) = 'A') and
                                  not(substring(Title,1,1) = 'B') and
                                  …
                                  not(substring(Title/,1,1) = 'Z')
                                  ]">
                <xsl:sort select="Title" />
                <xsl:appy-templates select="Title" />
                <!-- Add other relevant data here -->
            </xsl:for-each>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$letter" />
            <xsl:for-each select="//Book[substring(Title,1,1) = $letter]">
                <xsl:sort select="Title" />
                <xsl:appy-templates select="Title" />
                <!-- Add other relevant data here -->
            </xsl:for-each>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

…
#
我的问题 上面的代码工作正常,但是:

  • 手动循环每个字母会变得很长,尤其是必须重复两次。有没有办法简化这一点?类似于
    的东西,我可以在调用模板时使用它来设置参数

  • 有没有更简单的方法来选择所有不以字母开头的标题?类似于
    //Book[not(substring(Title,1,1)=[A-Z])

  • 在某些情况下,标题或作者姓名可能以小写字母开头。在上面的代码中,它们将被分组在#标题下,而不是实际的字母。我认为,手动操作的唯一方法将大大增加代码


  • 此解决方案回答了所有问题:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:variable name="vLowercase" select="'abcdefghijklmnopqrstuvuxyz'"/>
     <xsl:variable name="vUppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
     <xsl:variable name="vDigits" select="'0123456789'"/>
    
     <xsl:key name="kBookBy1stChar" match="Book"
              use="translate(substring(Title, 1, 1),
                             'abcdefghijklmnopqrstuvuxyz0123456789',
                             'ABCDEFGHIJKLMNOPQRSTUVWXYZ##########'
                             )"/>
    
      <xsl:template match="/*">
        <xsl:apply-templates mode="firstInGroup" select=
         "Book[generate-id()
              = generate-id(key('kBookBy1stChar',
                                translate(substring(Title, 1, 1),
                                          concat($vLowercase, $vDigits),
                                          concat($vUppercase, '##########')
                                          )
                                )[1]
                            )
              ]">
          <xsl:sort select="translate(substring(Title, 1, 1),
                                      concat($vLowercase, $vDigits),
                                      concat($vUppercase, '##########')
                                      )"/>
       </xsl:apply-templates>
      </xsl:template>
    
      <xsl:template match="Book" mode="firstInGroup">
        <xsl:value-of select="'&#xA;'"/>
       <xsl:value-of select="translate(substring(Title, 1, 1),
                                      concat($vLowercase, $vDigits),
                                      concat($vUppercase, '##########')
                                      )"/>
        <xsl:apply-templates select=
        "key('kBookBy1stChar',
             translate(substring(Title, 1, 1),
                       concat($vLowercase, $vDigits),
                       concat($vUppercase, '##########')
                       )
             )">
           <xsl:sort select="Title"/>
        </xsl:apply-templates>
      </xsl:template>
    
      <xsl:template match="Book">
        <xsl:value-of select="'&#xA;'"/>
        <xsl:value-of select="concat(Title, ' - ', Binding, ', $', price)"/>
      </xsl:template>
    </xsl:stylesheet>
    

    说明

    #
    1494 - hardcover, $9.99
    A
    After the Sands - paperback, $24.95
    Arctic Spirit - hardcover, $65.00
    B
    Back to the Front - paperback, $18.95
    C
    Cats Galore: A Compendium of Cultured Cats - hardcover, $5.00
    
  • 使用进行分组
  • 使用标准XPath函数
  • 使用处理以相同(不区分大小写)字符开头的一组书籍中的第一本
  • 使用按字母或字母顺序对书籍进行排序

  • 最有问题的部分是:

    我还需要一个部分的项目,不以字母开头,说一个数字或符号

    如果您有一个项目可以开始的所有可能符号的列表,那么您可以简单地使用
    translate()
    将它们全部转换为
    字符。否则它会变得更复杂。我会尝试以下方法:

    XSLT1.0(+EXSLT node-set())

    
    #
    
    
    - 
    , 
    
    
    
    
    

    但是,这仍然存在以变音字母开头的项目的问题,例如“Österreich”或希腊字母。在这种方法下,它们也将聚集在
    #

    不幸的是,解决这个问题的唯一好办法是使用XSLT2.0



    演示:

    这太棒了,谢谢!我对XSLT不是非常熟悉,所以我不知道Muenchian方法或
    模式
    。但这非常有效,并将帮助我简化代码的其他方面。@ShedSimas欢迎您。为了了解XSLT和XPath,我推荐现有的PluralsightXSLT1.0/2.0/3.0教程,以及XPath3.0教程。
    #
    1494 - hardcover, $9.99
    A
    After the Sands - paperback, $24.95
    Arctic Spirit - hardcover, $65.00
    B
    Back to the Front - paperback, $18.95
    C
    Cats Galore: A Compendium of Cultured Cats - hardcover, $5.00
    
    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl">
    <xsl:output method="text" encoding="UTF-8"/>
    
    <xsl:key name="book" match="Book" use="index" />
    
    <xsl:template match="/Books">
        <!-- first-pass: add index char -->
        <xsl:variable name="books-rtf">
            <xsl:for-each select="Book">
                <xsl:copy>
                    <xsl:copy-of select="*"/>
                    <index>
                        <xsl:variable name="index" select="translate(substring(Title, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
                        <xsl:choose>
                            <xsl:when test="contains('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $index)">
                                <xsl:value-of select="$index"/>
                            </xsl:when>
                            <xsl:otherwise>#</xsl:otherwise>
                        </xsl:choose>
                    </index>
                </xsl:copy>
            </xsl:for-each>
        </xsl:variable>
        <xsl:variable name="books" select="exsl:node-set($books-rtf)/Book" />
        <!-- group by index char -->
        <xsl:for-each select="$books[count(. | key('book', index)[1]) = 1]">
            <xsl:sort select="index"/>
            <xsl:value-of select="index"/>
            <xsl:text>&#10;</xsl:text>
            <!-- list books -->
            <xsl:for-each select="key('book', index)">
                <xsl:sort select="Title"/>
                <xsl:value-of select="Title"/>
                <xsl:text> - </xsl:text>
                <xsl:value-of select="Binding"/>
                <xsl:text>, </xsl:text>
                <xsl:value-of select="Price"/>
                <xsl:text>&#10;</xsl:text>
            </xsl:for-each>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:template>
    
    </xsl:stylesheet>