如何通过第二个(非命名)模板重新处理元素?

时间:2012-04-20 14:49:27

标签: xslt xpath

我已将某些语句分开应用于单独模板中的特定元素 元素在一个模板中处理,其中包含一个语句<xsl:apply-templates/> 有没有办法在第一个模板中处理刚刚处理的元素,在第二个(非命名)模板中重新处理?
当然我知道我可以使用一个命名模板并从第一个模板调用它,但我只是想知道它是否可以这样做。 为了给你一些工作,这里是我正在研究的东西的大大简化的模型。
我有一个包含表格的html输入文件:

<?xml version="1.0" encoding="UTF-8"?>
<table>
    <tbody>
        <tr>
            <td>content</td>
            <td></td>
            <td/>
        </tr>
    </tbody>
</table>

我希望每个block元素中都有一个td元素,如果有的话,它会包含td字符串内容;如果td元素本身是空的,则为空空。我尝试了以下内容(请再次注意,我不希望在第一个模板中进行重大更改!):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:attribute name="test"><xsl:value-of select="local-name()"/></xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="td/text()">
        <block><xsl:value-of select="."/></block>
    </xsl:template>
</xsl:stylesheet>

添加的属性只是在第一个模板中完成的事物的建模示例。

作为输出

<?xml version="1.0" encoding="UTF-8"?>
<table test="table">
    <tbody test="tbody">
        <tr test="tr">
            <td test="td">
                <block>content</block>
            </td>
            <td test="td"/>
            <td test="td"/>
        </tr>
    </tbody>
</table>

我希望得到

<?xml version="1.0" encoding="UTF-8"?>
<table test="table">
    <tbody test="tbody">
        <tr test="tr">
            <td test="td">
                <block>content</block>
            </td>
            <td test="td">
                <block/>
            </td>
            <td test="td">
                <block/>
            </td>
        </tr>
    </tbody>
</table>

很明显为什么它不起作用:空的td元素中没有文本节点,因此第二个模板的Xpath表达式不匹配这些节点。
所以我开始考虑通过第二个模板重新处理td元素本身的方法。当然,我不能使用match="td[some condition]",因为这样就不会应用第一个模板了 而且我也不能使用match= "self::td",因为模板匹配属性中不允许使用self轴(当然条件除外),或者更确切地说,它需要一个当前节点,当前它还没有开始匹配。
那么第二个模板是否有合适的Xpath表达式可以提供我想要的东西呢? 或者是否有另一种简单的方法可以触发刚刚在一个模板中处理的元素的另一个(非命名)模板? 注意:我不是在第二个模板上寻找简单的更新,在那里添加属性。我希望第一个模板添加属性,第二个模板添加块元素。

4 个答案:

答案 0 :(得分:2)

在XSLT 2.0中查看xsl:next-match或在XSLT 1.0中查看xsl:apply-imports

答案 1 :(得分:2)

尝试添加其他“模式”模板。您应该可以使用xsl:apply-templates模式。

示例xsl:template:

<xsl:template match="td" mode="test">
  <bar>test</bar>
</xsl:template>

示例xsl:apply-templates:

<xsl:template match="td">
  <foo><xsl:apply-templates select="ancestor-or-self::td" mode="test"/></foo>
</xsl:template>

答案 2 :(得分:1)

使用<xsl:apply-imports>

的XSLT 1.0解决方案
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:import href="block.xsl" />

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:attribute name="test"><xsl:value-of select="local-name()"/></xsl:attribute>
            <xsl:if test="self::td">
                <xsl:apply-imports/>
            </xsl:if>  
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

导入的样式表 block.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:template match="td">
        <block><xsl:value-of select="."/></block>
    </xsl:template>

</xsl:stylesheet>

使用<xsl:next-match>的XSLT 2.0解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:attribute name="test"><xsl:value-of select="local-name()"/></xsl:attribute>
            <xsl:if test="self::td">
                <!--apply the next template that would match this td element -->
                <xsl:next-match/>
            </xsl:if>  
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <!--calculated priority of "td" would be higher than "*",
        so explicitly setting a lower priority -->
    <xsl:template match="td" priority="-1">
        <block><xsl:value-of select="."/></block>
    </xsl:template>

</xsl:stylesheet>

答案 3 :(得分:0)

为了完整起见,我添加了DevNull回答的解决方案。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:attribute name="test"><xsl:value-of select="local-name()"/></xsl:attribute>
            <xsl:apply-templates/>
            <xsl:apply-templates mode="td" select="self::td"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="td" mode="td">
        <block><xsl:value-of select="."/></block>
    </xsl:template>

    <xsl:template match="td/text()"/>
</xsl:stylesheet>

导致

<?xml version="1.0" encoding="UTF-8"?>
<table test="table">
    <tbody test="tbody">
        <tr test="tr">
            <td test="td">
                <block>content</block>
            </td>
            <td test="td">
                <block></block>
            </td>
            <td test="td">
                <block></block>
            </td>
        </tr>
    </tbody>
</table>

请注意添加的模板,以防止默认情况下第二次处理td文本节点模板。