XSLT 中的书籍标题排序






4.38/5 (12投票s)
使用 XSL 实现自定义排序顺序的示例 - 在本例中,按第一个非冠词单词对标题进行排序。
引言
我的一个网站允许人们发布他们的短篇故事,我一直很困扰一件事:当标题按字母顺序排序时,我会得到一个长长的以“T”开头的列表,所有以“The”开头的标题都会聚集在那里。
传统图书馆使用一种称为“语法”的排序顺序,其中标题根据第一个重要词进行分类。理论上,这个词可以是标题中的任何一个词,但如今实际上,它通常是非冠词(即 A/An 或 The)的第一个词。所以,而不是这个简单的字母列表
- 双城记
- 波士顿人
- 认真的重要性
- 战争与和平
标题应按如下方式排序
- 波士顿人
- 认真的重要性
- 双城记
- 战争与和平
(事实上,为了减少混淆,标题可以列为,例如,“双城记,A”,但我将在本文中只关注排序。)
当我研究如何在我的网站上实现这一点的方法时,我很快发现 XSL 的 <xsl:sort>
元素是解决我问题的方案,因为它可以用于为一组 XML 数据创建完全自定义的排序顺序。网络上可以找到与此类似的解决方案(例如此处),但它们通常不包含对这些方法如何工作的解释。本文其余部分将解释一种构建 select
语句以执行此类标题排序的简单方法。
自定义 xsl:sort
在下面的示例中,我将假设我们正在处理具有以下结构的 XML 源(完整的 XML 文件和相应的样式表包含在源 zip 文件中)
<Stories>
<Story>
<Title>War and Peace</Title>
</Story>
<Story>
<Title>The Bostonians</Title>
</Story>
...
</Stories>
为了按完整标题对这些元素进行排序,您只需使用如下代码
<xsl:for-each select="Story">
<xsl:sort select="Title"/>
<xsl:value-of select="Title"/>
</xsl:for-each>
当存在 xsl:sort
元素时,XSLT 处理器通过评估要排序的每个元素的 select
语句的结果来确定排序顺序。在这种情况下,当它遍历 Story 元素时,处理器将检查 Title 的值,并确定该特定元素在排序列表中的位置。结果将是一个简单的字母列表,类似于引言中的第一个列表。
然而,我们实际上希望某些标题根据第二个词而不是第一个词进行排序,在这些情况下,我们需要处理器从第一个空格开始评估 Title 字符串。substring-after
函数非常适合此目的
<xsl:sort select="substring-after(Title, ' ')"/>
substring-after(Title, ' ')
的结果将是 Title 元素中第一个空格之后的所有内容。当它遍历元素时,XSLT 处理器将对这组内容进行排序
- 与和平
- 波士顿人
- 认真的重要性
- 双城记
不幸的是,虽然这确实会将波士顿人放在“B”下,但它也会将战争与和平放在“A”下。更糟糕的是,不包含任何空格的标题最终会未排序地出现在列表开头,因为在这种情况下,该函数根本不返回任何内容,因此处理器不会将其包含在排序列表中。
我们需要更具体地说明哪些标题需要根据第二个词进行排序。幸运的是,substring
函数可以提供帮助。以下函数的结果将是第一个空格之后的所有内容,但前提是 Title 元素以“The ”开头(我们需要包含“The”后的空格,以免像“Thesaurus”这样的标题被包含在内)
substring(substring-after(Title, ' '), 0 div starts-with(Title, 'The '))
substring
函数的第二个参数通常接受一个数字,该数字确定子字符串的起始位置,但它还有一个额外的好处,即如果提供了无效数字,该函数将返回所有内容。这里将其与 starts-with
函数结合使用,该函数如果字符串以“The ”开头则返回布尔值 true
,否则返回 false
。将 true
和 false
转换为数字时,它们分别等于 1 和 0。
在这种情况下,将 0 除以 starts-with
函数返回的布尔值会根据标题是否以“The ”开头,将值在 0 (0 div 1) 和 NaN (0 div 0 - 非数字) 之间切换。如果值为 0,则函数返回标题减去开头的词,因此它将按第二个词排序,如上所述。但是,如果值为 NaN,则函数不返回任何内容,因此标题未排序,并且出现在 XML 文档中的相同位置。
此时,我们可以正确地对以“The ”开头的标题进行排序,因此我们现在还需要对其他标题进行排序。这可以通过类似的 substring
函数完成
substring(Title, 0 div not(starts-with(Title, 'The ')))
在这种情况下,第一个参数就是 Title,因为我们确实希望这些标题按元素值的完整值进行排序。第二个参数依赖于与上述相同的评估,以根据标题不以“The ”开头的值生成 0 或 NaN。
现在我们有了对普通标题进行字母排序和对以 The 开头的标题按第二个词进行排序的函数。下一步是将它们组合起来。由于两个 substring
函数是互斥的,我们可以使用 concat
函数将它们连接起来
<xsl:sort select="concat(substring(substring-after(Title, ' '),
0 div starts-with(Title, 'The ')),
substring(Title, 0 div not(starts-with(Title, 'The '))))"/>
结果是一个已正确排序的标题列表。通过在 substring
函数中添加额外的条件,可以将此功能进一步扩展,如下例所示
<xsl:sort select="concat(substring(substring-after(Title, ' '),
0 div boolean(starts-with(Title, 'A ') or starts-with(Title, 'An ')
or starts-with(Title, 'The '))),
substring(Title, 0 div not(starts-with(Title, 'A ')
or starts-with(Title, 'An ') or starts-with(Title, 'The '))))"/>
关注点
虽然 XSL 语言有时可能非常繁琐且冗长,但它通过极其强大的功能弥补了这一点。在设计我的网站时,我能够实现一些仅用 ASP 和 ADO 就非常难以实现的功能。我希望这个例子能为您自定义排序提供有用的介绍。
历史
- 2006 年 1 月 - 第一个版本。