首页 > 解决方案 > 用于项目符号点元素的 XSLT 动态标记插入

问题描述

我才刚刚开始了解 XSLT 并查看 W3school 示例来解决我的雇主给我的新任务。

我有一些文件需要用 XML 结构进行转换,如下所示。

     <tablecell bgcolor='white'>
<pardef id='16' leftmargin='0.2757in' list='bullet' keepwithnext='true' keeptogether='true'/>
        <par def='34'>
            <run>
                <font size='8pt' name='Verdana' pitch='variable' truetype='true'
     familyid='20' color='navy'/>Data in bullet point 1</run>
        </par>
        <par def='34'>
            <run>
                <font size='8pt' name='Verdana' pitch='variable' truetype='true'
     familyid='20' color='navy'/>Data in bullet point 2</run>
        </par>
        <par def='34'>
            <run>
                <font size='8pt' name='Verdana' pitch='variable' truetype='true'
     familyid='20' color='navy'/>Data in bullet point 3</run>
        </par>
        <par def='34'>
            <run>
                <font size='8pt' name='Verdana' pitch='variable' truetype='true'
     familyid='20' color='navy'/>Data in bullet point 4</run>
        </par>
    </tablecell>

到目前为止,我的 XSL 处理 xml 的内容如下。

    <xsl:template match="tablecell">
            <td>
                <xsl:copy-of select="@colspan" />
                <!--<xsl:value-of select="."/> -->
                <xsl:apply-templates select="table" />
                <xsl:apply-templates select="section" />
                <xsl:apply-templates select="par" />
                <xsl:apply-templates select="pardef" />
            </td>
    </xsl:template>

<xsl:template match="pardef">
        <xsl:if test="@list='bullet'">
            <ul>
                <xsl:apply-templates/>
         </xsl:if>
    </xsl:template>

    <xsl:template match="par">
            <p>
                <xsl:apply-templates select="run" />
            </p>
    </xsl:template>

    <xsl:template match="run">
            <li>
                <xsl:apply-templates select="run" />
            </li>
    </xsl:template>

我遇到的问题是我不确定</ul>在最后一个元素之后动态插入结束标记<run>以创建项目符号的最佳方法。在实践中,我的 XML 文件中可能有任意数量的<run>标签需要转换。

这是我需要对变量做某事并计算元素数量然后基于此变量进行一些处理的地方吗?

我需要指导的另一种情况是,如果标签位于 pardef/par 结构下,其中 pardef 元素具有属性 list='bullet' ,则<run>标签只能被标签包围。<li>

这是我想要达到的输出。

<p>
    <ul>
        <li>Data in bullet point 1</li>
        <li>Data in bullet point 2</li>
        <li>Data in bullet point 3</li>
        <li>Data in bullet point 4</li>
    </ul>  
</p>

如果您能为上述两个查询指明正确的方向,我们将不胜感激。

干杯


除了编辑现有线程之外,不确定如何使用更多代码片段继续这个现有线程。如果你能让我知道该怎么做,那就太好了。

感谢所有帮助解决这个问题的人。之前有人回复了这篇文章,提出了使用模式的想法,这帮助我走得更远。我现在遇到的问题是以下情况,如果<par>遇到带有属性 def=16 的标签,我希望它使用 mode='sixteen' 模板,否则使用非常简单地使用<p> </p>标签的通用基本 par 模板。如何实现这一目标?现在我正在考虑使用 if 语句,但这会是最好的做法吗?

另外,下面的代码是否意味着<par>元素可能被处理两次?

<xsl:template match="tablecell">
        <td>
            <!-- <xsl:copy-of select="@colspan" /> -->
            <xsl:apply-templates select="par" />
            <xsl:apply-templates select="table" />
            <xsl:apply-templates select="section" />

            <!-- Apply to par elements where attribute def=16 -->   
            <xsl:apply-templates select="par[@def='16']" mode='sixteen' />
        </td>
    </xsl:template>

    <!-- Template for par elements where attribute def=16 -->
    <xsl:template match="par" mode='sixteen'>
        <ul>
            <!-- Apply to any table elements -->    
            <xsl:apply-templates select="run" mode='bullet' />
        </ul>
    </xsl:template>

这是我的尝试,不确定它是否正确或最佳实践。

  <xsl:template match="tablecell">
        <td>
            <xsl:choose>
                <xsl:when test="par[@def='16']">
                    <!-- Apply to par elements where attribute def=16 -->   
                    <xsl:apply-templates select="par[@def='16']" mode='sixteen' />

                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="par" />

                </xsl:otherwise>


            </xsl:choose>
<xsl:apply-templates select="table" />
                <xsl:apply-templates select="section" />
        </td>
    </xsl:template>

标签: xmlxslt

解决方案


您缺少的关键点是 XSLT 不会在输出中创建开始和结束标记,它会创建节点树。您不能将半个节点写入树。类似“文字结果元素”<ul> XXX </ul>是一条指令,它 (a) 评估 XXX 以创建一些内容,并且 (b) 创建一个将内容附加到的 UL 元素节点。您必须将其<ul></ul>视为构建节点的指令,而不是创建开始标记的指令后跟创建结束标记的指令。

您的问题是一个经典的位置分组问题。事实上,它是 XSLT 规范中用于指令的示例之一:在https://www.w3.org/TR/xslt-30/#element-for-eachxsl:for-each-group中搜索“Example: Grouping Alternating Sequences of Elements” -组。请注意,此指令需要 XSLT 2.0 或 XSLT 3.0。在许多环境中,默认的 XSLT 处理器仍然只支持 XSLT 1.0。在 1.0 中解决这个问题要困难得多(这并非不可能,但对于该语言的新手来说是一个重大挑战)。因此,请确保您使用的是支持 2.0 或 3.0 的处理器。


推荐阅读