如何修改XSL将含h标签的扁平XML转换为层级XML
适配XSL将扁平h/p结构转换为层级section结构
我懂你的痛点——之前的XSL是为带class的标题p标签写的,现在你把标题换成了h标签,层级用h的class(head1/head2/head3)区分,原代码肯定没法直接用了。咱们来调整代码,完美适配你的新结构:
修改后的完整XSL代码
<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="xs mf" version="2.0"> <xsl:output indent="yes"/> <!-- 身份模板:复制所有未被匹配的节点 --> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> <!-- 递归分组函数:处理指定层级的标题及下属内容/子标题 --> <xsl:function name="mf:group" as="element(section)*"> <xsl:param name="entries" as="element()*"/> <xsl:param name="level" as="xs:integer"/> <xsl:for-each-group select="$entries" group-starting-with="h[@class = concat('head',$level)]"> <!-- 只有当分组起始节点是当前层级的标题时才创建section --> <xsl:if test="self::h[@class = concat('head',$level)]"> <xsl:variable name="current-heading-id" select="generate-id(.)"/> <section name="{@class}"> <title> <xsl:value-of select="."/> </title> <!-- 收集当前标题之后、下一个标题之前的所有p内容 --> <xsl:variable name="content-paragraphs" select="following-sibling::p[generate-id(preceding-sibling::h[@class][1]) = $current-heading-id]"/> <xsl:if test="$content-paragraphs"> <ps> <xsl:apply-templates select="$content-paragraphs"/> </ps> </xsl:if> <!-- 递归处理当前分组中除了当前标题的其他节点,处理下一层级标题 --> <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/> </section> </xsl:if> </xsl:for-each-group> </xsl:function> <!-- 匹配body节点,启动分组逻辑 --> <xsl:template match="body"> <xsl:copy> <!-- 传入所有h(标题)和p(内容)节点,从level 1开始分组 --> <xsl:sequence select="mf:group(*[self::h or self::p], 1)"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
关键修改点说明
- 函数参数类型更新:把
mf:group的第一个参数从element(p)*改成element()*,这样能同时处理h标题和p内容节点。 - 分组起始节点调整:将原代码中匹配
p[@class]的地方全部换成h[@class],因为现在标题是h标签了。 - body模板调用优化:传入
*[self::h or self::p],确保所有标题和内容节点都被纳入分组处理,而不是只筛选带head的节点。 - 内容段落选择修正:调整了p内容节点的匹配逻辑,确保只选中当前标题到下一个标题之间的段落,避免跨层级错误。
- 增加分组有效性判断:加了
<xsl:if test="self::h[@class = concat('head',$level)]",防止非目标节点触发无效的section创建。
验证效果
用你提供的输入XML运行修改后的XSL,就能生成你想要的嵌套section结构:
<body> <section name="head1"> <title>1: Heading level 1</title> <ps> <p>some text here</p> <p>some text here</p> </ps> <section name="head2"> <title>1.1: Heading level 2</title> <ps> <p>some text here</p> <p>some text here</p> </ps> <section name="head3"> <title>1.1.1: Heading level 3</title> <ps> <p>some text here</p> <p>some text here</p> </ps> </section> </section> </section> <section name="head1"> <title>2: Heading level 1</title> <section name="head2"> <title>2.1: Heading level 2</title> <ps> <p>some text here</p> <p>some text here</p> </ps> <section name="head3"> <title>2.1.1: Heading level 3</title> <ps> <p>some text here</p> <p>some text here</p> </ps> </section> <section name="head3"> <title>2.1.2: Heading level 3</title> <ps> <p>some text here</p> <p>some text here</p> </ps> </section> </section> </section> </body>
内容的提问来源于stack exchange,提问作者Stéphane Garcia
相关产品推荐
相关产品推荐

