使用XSLT 3将嵌套XML的levelledPara转为非嵌套编号HTML列表
问题需求
需要将XML中嵌套的levelledPara元素转换为HTML的非嵌套编号列表,要求每个列表项以flex布局展示,编号列宽度固定为21mm。当前使用的XSLT代码输出存在嵌套结构,无法达到预期效果。
输入XML
<dmodule> <content> <crew> <descrCrew> <levelledPara> <title>Introduction</title> <para>Data about the bicycle and its control system is given in this document. This data will help you operate the bicycle.</para> </levelledPara> <levelledPara> <levelledPara> <title>Controls</title> <para>Data about the controls that follow is given in this document: </para> <levelledPara> <title>Level 3</title> <para>Some text to put here for level 3 levelledpara.</para> </levelledPara> </levelledPara> <levelledPara id="par-0001"> <title>Steering</title> <para>The handlebars are used to steer the bike. They are at the front of the bicycle. You hold one of the handlebar grips with each hand and move the handle bar to change the direction of the bike.</para> </levelledPara> <levelledPara id="par-0002"> <title>Shifters</title> <para>The gears control the ratio of pedal rotation to wheel rotation. You can change this with the shifters. The shifters are on the handlebar.</para> <para>A description of the two shifters follows.</para> </levelledPara> </levelledPara> </descrCrew> </crew> </content> </dmodule>
预期HTML输出
<div> <div style="display: flex;"> <div style="width:21mm;">1</div> <div><span class="Sidehead1">Introduction</span><p>Data about the bicycle and its control system is given in this document. This data will help you operate the bicycle.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.1</div> <div><span class="Sidehead2">Controls</span><p>Data about the controls that follow is given in this document.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.1.1</div> <div><span class="Sidehead3">Level 3</span><p>Some text to put here for level 3 levelledpara.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.2</div> <div><span class="Sidehead2">Steering</span><p>The handlebars are used to steer the bike. They are at the front of the bicycle. You hold one of the handlebar grips with each hand and move the handle bar to change the direction of the bike.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.3</div> <div><span class="Sidehead2">Shifters</span><p>The gears control the ratio of pedal rotation to wheel rotation. You can change this with the shifters. The shifters are on the handlebar.</p> <p>A description of the two shifters follows.</p> </div> </div> </div>
当前实际HTML输出
<div style="display: flex;"> <div style="width:21mm;">1</div> <div><span class="Sidehead1">Introduction</span><p>Data about the bicycle and its control system is given in this document. This data will help you operate the bicycle.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;"></div> <div> <div style="display: flex;"> <div style="width:21mm;">2.1</div> <div><span class="Sidehead2">Controls</span><p>Data about the controls that follow is given in this document: </p> <div style="display: flex;"> <div style="width:21mm;">2.1.1</div> <div><span class="Sidehead3">Level 3</span><p>Some text to put here for level 3 levelledpara.</p> </div> </div> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.2</div> <div><span class="Sidehead2">Steering</span><p>The handlebars are used to steer the bike. They are at the front of the bicycle. You hold one of the handlebar grips with each hand and move the handle bar to change the direction of the bike.</p> </div> </div> <div style="display: flex;"> <div style="width:21mm;">2.3</div> <div><span class="Sidehead2">Shifters</span><p>The gears control the ratio of pedal rotation to wheel rotation. You can change this with the shifters. The shifters are on the handlebar.</p> <p>A description of the two shifters follows.</p> </div> </div> </div> </div>
当前使用的XSLT代码
<xsl:template match="levelledPara[title]"> <div style="display: flex;"> <div style="width:21mm;"> <xsl:number count="levelledPara" from="content" level="multiple" format="1.1.1.1.1"/> </div> <div> <xsl:apply-templates/> </div> </div> </xsl:template> <xsl:template match="levelledPara"> <div style="display: flex;"> <!-- suppress numbering if parent levelledPara doesn't have a title --> <div style="width:21mm;"> <xsl:if test="not(levelledPara/title)"> <xsl:number count="levelledPara" from="content" level="multiple" format="1.1.1.1.1"/> </xsl:if> </div> <div> <xsl:apply-templates/> </div> </div> </xsl:template>
解决方案
当前代码的问题在于递归处理嵌套的levelledPara,导致生成嵌套的div结构。要实现非嵌套的输出,需要先扁平化所有带title的levelledPara元素,然后逐个生成对应的flex行。
修改后的XSLT代码如下:
<xsl:template match="/dmodule/content"> <div> <!-- 遍历所有带title的levelledPara,无论嵌套层级 --> <xsl:for-each select="//levelledPara[title]"> <div style="display: flex;"> <div style="width:21mm;"> <!-- 生成多级编号 --> <xsl:number count="levelledPara" from="content" level="multiple" format="1.1.1"/> </div> <div> <!-- 根据层级生成对应的Sidehead类 --> <span class="Sidehead{count(ancestor::levelledPara) + 1}"> <xsl:value-of select="title"/> </span> <!-- 处理当前levelledPara下的para元素,排除子levelledPara --> <xsl:apply-templates select="para"/> </div> </div> </xsl:for-each> </div> </xsl:template> <!-- 处理para元素生成p标签 --> <xsl:template match="para"> <p> <xsl:value-of select="normalize-space(.)"/> </p> </xsl:template> <!-- 忽略其他未匹配的元素,避免默认输出 --> <xsl:template match="text()[not(parent::para or parent::title)]"/>
代码说明:
- 主模板匹配
/dmodule/content,生成外层包裹的div。 - 使用
//levelledPara[title]选中所有带标题的levelledPara,实现扁平化遍历,不管嵌套层级。 - 用
<xsl:number>生成正确的多级编号,count="levelledPara"统计所有层级的levelledPara,level="multiple"生成多级格式。 - 通过
count(ancestor::levelledPara) + 1计算当前元素的层级,生成对应的SideheadN类。 - 单独处理
para元素,生成<p>标签,同时忽略无关的文本节点,避免多余输出。
这样就能生成完全非嵌套的flex布局列表,符合预期输出要求。
内容的提问来源于stack exchange,提问作者Caroline
相关产品推荐
相关产品推荐

