提问人:fauve 提问时间:10/2/2023 更新时间:10/2/2023 访问量:79
在 XSLT 中使 ul li 结构内标题节点的 TOC
Make in XSLT a TOC of the heading nodes inside an ul li structure
问:
总体概览
我尝试通过仅选择他的标题节点(如 h1、h2)来制作文档的目录 (TOC) ......h9 ()。h[0-9]
这些标题节点应该以级联结构进行结构 - 结构,其中所有依赖 an 的节点都应该包含在 的 中。<ul>
<li>
<hN+1>
<hN>
<ul>
<li>
<hN>
例
为了更明确,让我们看看这个例子。如果我有以下文件:document.xsl
<?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>
<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>
</document>
那么预期的渲染应该是:
<ul>
<li>
<span>Lorem <i>arepo</i> ispum 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></li>
</ul>
<li>
</ul>
最小的工作示例
目前,我有这个maketoc.xslt
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="document">
<ul>
<xsl:apply-templates select="*[self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6 | self::h7 | self::h8 | self::h9]"/>
</ul>
</xsl:template>
<xsl:template match="h1 | h2 | h3 | h4 | h5 | h6 | h7 | h8 | h9">
<li>
<span><xsl:value-of select="."/></span>
<ul>
<xsl:apply-templates select="following-sibling::*[1][self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6 | self::h7 | self::h8 | self::h9]"/>
</ul>
</li>
</xsl:template>
<!-- Ignor anything else -->
<xsl:template match="*"/>
</xsl:stylesheet>
当前渲染
但是,这个 MWE 生成以下输出:
<ul>
<li>
<span>Lorem arepo ipsum dolor</span>
<ul>
<li>
<span>Lorem ipsum dolor</span>
<ul/>
</li>
</ul>
</li>
<li>
<span>Lorem ipsum dolor</span>
<ul/>
</li>
<li>
<span>sit amet et consectetur</span>
<ul>
<li>
<span>Quia adipit</span>
<ul>
<li>
<span>aliquam quaerat</span>
<ul/>
</li>
</ul>
</li>
</ul>
</li>
<li>
<span>Quia adipit</span>
<ul>
<li>
<span>aliquam quaerat</span>
<ul/>
</li>
</ul>
</li>
<li>
<span>aliquam quaerat</span>
<ul/>
</li>
<li>
<span>Erit et nunquam</span>
<ul/>
</li>
</ul>
问题
如您所见,此 MBE 存在一些问题:
- 对同一同级中的第一个降序节点进行多次处理。它们的处理量与它们的深度一样多。例如,“aliquam quaerat”出现树时间,因为他是一个节点。
<h3>
- 一个无用的人有时会出现在一个没有里面的人里面。
</ul>
<li>
<ul>
- 不是兄弟姐妹之间第一个节点的节点被视为 .(参见“Erit et nunquam”的案例,他是.
<h1>
<h2>
问题
如何使xml的标题节点在级联结构中与后代与其上升者密切相关?<ul>
<li>
答:
假设 XSLT 3(或 2 但以下版本使用某些 XSLT 3),这是使用以下递归分组的任务:for-each-group group-starting-with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:group" as="node()*">
<xsl:param name="headings" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$headings" group-starting-with="*[local-name() = 'h' || $level]">
<xsl:if test="self::*[local-name() = 'h' || $level]">
<li>
<span>
<xsl:apply-templates/>
</span>
<xsl:where-populated>
<ul>
<xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
</ul>
</xsl:where-populated>
</li>
</xsl:if>
</xsl:for-each-group>
</xsl:function>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="document">
<ul>
<xsl:sequence select="mf:group(*[self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6 | self::h7 | self::h8 | self::h9], 1)"/>
</ul>
</xsl:template>
</xsl:stylesheet>
从您的原始代码中选择,我可能倾向于将其写成 .<xsl:sequence select="mf:group(*[self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6 | self::h7 | self::h8 | self::h9], 1)"/>
<xsl:sequence select="mf:group(h1 | h2 | h3 | h4 | h5 | h6 | h7 | h8 | h9, 1)"/>
至于您的评论似乎使用不支持 XSLT 3.0 的旧版本 Saxon,您需要将 XPath 3 表达式替换为 XPath 3 函数,然后,对于 ,您需要通过替换整体和内容来做更多的工作,例如'h' || $level
concat('h', $level)
tail(current-group())
subsequence(current-group(), 2)
xsl:where-populated
xsl:where-populated
<xsl:variable name="tail-of-group" select="subsequence(current-group(), 2)"/>
<xsl:if test="$tail-of-group">
<ul>
<xsl:sequence select="mf:group($tail-of-group, $level + 1)"/>
</ul>
</xsl:if>
XSLT 3 声明需要在 XSLT 2 中通过拼写<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@*, node()"/>
</xsl:copy>
</xsl:template>
评论
saxon-xslt -o output.txt document.xml maketoc1.xslt and I get
我已将答案从将平面层次结构转换为 XSLT 深度的嵌套层次结构,以适应此处提出的问题。
这是一个 XSLT 1.0 解决方案,但对我来说,它似乎比另一个答案中建议的 XSLT 2.0/3.0 方法更优雅。
<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>
<ul>
<xsl:apply-templates select="key('child', generate-id())"/>
</ul>
</li>
</xsl:template>
</xsl:stylesheet>
评论
name(current()), 'h' -1
评论
h6