如何用XSLT将带style的p标签转为li并生成HTML5合规结构?
解决XSLT分组转换blockquote内p标签的问题
需求说明
- 将
<blockquote>内带@style属性的<p>标签转换为<li>元素 - 不带
@style属性的<p>标签保持原有结构 - 连续的带
@style的<p>需被包裹在独立的<ul>中,不带@style的<p>作为外层<div>的直接子元素,避免出现<p>作为<ul>子元素的不规范结构
输入内容
<blockquote> <p style="margin-left: 40px;">test</p> <p style="margin-left: 40px;">some test</p> <p style="margin-left: 40px;">another test</p> <p> <em>paragraph</em> </p> <p style="margin-left: 40px;">a</p> <p style="margin-left: 40px;">b</p> <p style="margin-left: 40px;">c</p> <p> <em>another paragraph</em> </p> <p style="margin-left: 40px;">qwert</p> <p style="margin-left: 40px;">asdf</p> <p style="margin-left: 40px;">sdfg</p> <p> <em>another para</em> </p> <p style="margin-left: 40px;">sdfg</p> <p style="margin-left: 40px;">asdf</p> <p style="margin-left: 40px;">qwert</p> </blockquote>
现有XSLT模板
<xsl:template match="blockquote"> <xsl:element name="div"> <xsl:attribute name="class" select="'section-content'"/> <xsl:attribute name="data-class" select="'blockquote'"/> <xsl:attribute name="data-unlock" select="'ALL'"/> <xsl:element name="ul"> <xsl:apply-templates/> </xsl:element> </xsl:element> </xsl:template> <xsl:template match="blockquote/p[@style]"> <xsl:element name="li"> <xsl:apply-templates/> </xsl:element> </xsl:template> <xsl:template match="blockquote/p[not(@style)]"> <xsl:element name="p"> <xsl:apply-templates/> </xsl:element> </xsl:template>
当前输出问题
现有模板会将所有内容包裹在一个<ul>中,导致不带@style的<p>成为<ul>的子元素,违反HTML5规范,输出结构如下:
<div class="section-content" data-class="blockquote" data-unlock="ALL"> <ul> <li>test</li> <li>some test</li> <li>another test</li> <p> <i>paragraph</i> </p> <li>a</li> <li>b</li> <li>c</li> <p> <i>another paragraph</i> </p> <li>qwert</li> <li>asdf</li> <li>sdfg</li> <p> <i>another para</i> </p> <li>sdfg</li> <li>asdf</li> <li>qwert</li> </ul> </div>
期望输出
连续的带@style的<p>被包裹在独立<ul>中,不带@style的<p>作为外层<div>的直接子元素:
<div class="section-content" data-class="blockquote" data-unlock="ALL"> <ul> <li>test</li> <li>some test</li> <li>another test</li> </ul> <p> <i>paragraph</i> </p> <ul> <li>a</li> <li>b</li> <li>c</li> </ul> <p> <i>another paragraph</i> </p> <ul> <li>qwert</li> <li>asdf</li> <li>sdfg</li> </ul> <p> <i>another para</i> </p> <ul> <li>sdfg</li> <li>asdf</li> <li>qwert</li> </ul> </div>
解决方案
需要使用XSLT的分组功能(xsl:for-each-group)对连续的带@style的<p>进行分组,替换原有的<blockquote>模板,同时保留原有针对<p>的模板:
<xsl:template match="blockquote"> <div class="section-content" data-class="blockquote" data-unlock="ALL"> <!-- 按相邻节点分组:区分带style的p和不带style的p --> <xsl:for-each-group select="*" group-adjacent="boolean(self::p[@style])"> <xsl:choose> <!-- 如果是连续的带style的p,包裹到ul中 --> <xsl:when test="current-grouping-key()"> <ul> <xsl:apply-templates select="current-group()"/> </ul> </xsl:when> <!-- 不带style的p直接输出 --> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </div> </xsl:template> <!-- 保留原有带style的p转li的模板 --> <xsl:template match="blockquote/p[@style]"> <li> <xsl:apply-templates/> </li> </xsl:template> <!-- 保留原有不带style的p的模板 --> <xsl:template match="blockquote/p[not(@style)]"> <p> <xsl:apply-templates/> </p> </xsl:template>
思路说明
- 使用
xsl:for-each-group的group-adjacent属性,按节点是否为带@style的<p>进行相邻分组 - 对于分组结果,带
@style的连续节点组被包裹在<ul>中,每个<p>转为<li> - 不带
@style的节点组直接输出,作为外层<div>的直接子元素,避免了不规范的嵌套结构
内容的提问来源于stack exchange,提问作者Bakhovuddin Latifjanov
相关产品推荐
相关产品推荐

