如何用XSLT将带层级间隙的扁平标题转为嵌套UL-LI目录
概述
本问题是之前某问题的延续,用于完善相关实现。
目标是将扁平结构的<h[0-6]>标题转换为嵌套的<ul>-<li>结构,以生成目录(TOC)。
目前的实现可完成基本转换,但无法处理层级间隙,例如文档直接从<h2>跳转到<h4>而无中间<h3>的情况。
当前状态
document.xml
<?xml version="1.0" encoding="UTF-8"?> <document> <h1>Lorem <i>arepo</i> ipsum dolor</h1> <h2>Lorem ipsum dolor</h2> <p> Sed ut <i>perspiciatis</i> unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? </p> <h1>sit amet et consectetur</h1> <h2>Quia adipit</h2> <h3>aliquam quaerat</h3> <h6>-HERE-</h6> <p> Sed ut <i>perspiciatis</i> unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? </p> <h2>Erit et nunquam</h2> <h3>corporis suscipit</h3> </document>
maketoc.xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:key name="child" match="h2|h3|h4|h5|h6" use="generate-id(preceding-sibling::*[name()=concat('h', substring-after(name(current()), 'h') - 1)][1])"/> <xsl:template match="/document"> <ul> <xsl:apply-templates select="h1"/> </ul> </xsl:template> <xsl:template match="h1|h2|h3|h4|h5|h6"> <li> <span> <xsl:copy-of select="node()" /> </span> <xsl:variable name="childElements" select="key('child', generate-id())"/> <xsl:if test="$childElements"> <ul> <xsl:apply-templates select="$childElements"/> </ul> </xsl:if> </li> </xsl:template> </xsl:stylesheet>
当前输出
<ul> <li><span>Lorem <i>arepo</i> ipsum dolor</span><ul> <li><span>Lorem ipsum dolor</span></li> </ul> </li> <li><span>sit amet et consectetur</span><ul> <li><span>Quia adipit</span><ul> <li><span>aliquam quaerat</span></li> </ul> </li> <li><span>Erit et nunquam</span><ul> <li><span>corporis suscipit</span></li> </ul> </li> </ul> </li> </ul>
此示例中,元素根据深度正确嵌套,但<h6>-HERE-</h6>节点因无前驱<h5>(且即使有<h5>,其也需前驱<h4>)而被忽略,文档直接从<h3>跳转到<h6>。
期望输出
<ul> <li><span>Lorem <i>arepo</i> ipsum dolor</span><ul> <li><span>Lorem ipsum dolor</span></li> </ul> </li> <li><span>sit amet et consectetur</span><ul> <li><span>Quia adipit</span><ul> <li><span>aliquam quaerat</span><ul> <li><span>-HERE-</span></li> <!-- 这是关键需求 --> </ul> </li> </ul> </li> <li><span>Erit et nunquam</span><ul> <li><span>corporis suscipit</span></li> </ul> </li> </ul> </li> </ul>
问题分析
如前所述,当前实现完全忽略层级间隙,而这些间隙应被纳入最终目录。
问题出在以下代码行:
<xsl:key name="child" match="h2|h3|h4|h5|h6" use="generate-id(preceding-sibling::*[name()=concat('h', substring-after(name(current()), 'h') - 1)][1])"/>
尤其是name(current()), 'h') - 1中的减法运算符,它仅匹配严格低一级的标题(即HTML层级中更高的标题)。若使用<=运算符匹配层级更高的最近前驱标签,将更符合需求。
相关讨论
- 已有针对XSLT 1.0从扁平带间隙结构转嵌套层级的解决方案,但结构与本文差异较大,难以适配。
- 关于XSLT中按深度将扁平层级转嵌套层级的讨论
问题
如何实现标题节点的嵌套<ul>-<li>层级结构,同时处理层级间隙?
内容的提问来源于stack exchange,提问作者fauve
相关产品推荐
相关产品推荐

