XSLT根据条件将潜在的多个模板应用于单个节点

问题描述:

以下是我的需求: 我有一个由多个嵌套SPAN组成的HTML文本。 一些跨度包括内联CSS。在内联CSS中,我需要将某些样式属性转换为“古典”HTML。 某些跨度有一个或多个要被HTML元素替换的属性。XSLT根据条件将潜在的多个模板应用于单个节点

下面是一个例子输入:

<HTML> 
<SPAN> 
    Some sentance including another 
    <SPAN STYLE="font-weight: bold;" > 
     included bold block 
    </SPAN> 
    with a tail in it and line breaks<BR/> 
</SPAN> 
<SPAN STYLE= "font-family: 'Helvetica';font-weight: bold;" > 
    Another span with 1 attribute not to be taken into account and 1 to be 
</SPAN> 
<SPAN STYLE= "font-family: 'Helvetica';font-weight: bold;text-decoration:underline;" > 
    Another span with two attributes to be taken into account 
</SPAN> 
</HTML> 

我想结果是什么:

<HTML> 
<p>Some sentance including another 
    <p><b>included bold block </b></p> 
    with a tail in it and line breaks<BR/></p> 
<p><b>Another span with 1 attribute not to be taken into account and 1 to be</b></p> 
<p><b><u>Another span with two attributes to be taken into account</u></b></p> 
</HTML> 

我认为这样做是使用的身份转换和最好的方式具有与如下条件匹配属性的模板:

SPAN[contains(@STYLE, 'font-weight: bold;') 

和:

SPAN[contains(@STYLE, 'text-decoration:underline;')] 

这里是我试过......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" omit-xml-declaration="yes"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'text-decoration:underline;')]"> 
     <xsl:element name="u"> 
      <xsl:apply-templates/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'font-weight: bold;')]"> 
     <xsl:element name="b"> 
      <xsl:apply-templates/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="SPAN"> 
     <!-- replacing SPAN into <p>elements --> 
     <xsl:element name="p"> 
      <xsl:apply-templates/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="SPAN/@STYLE"/> 
    <!-- suppression of the old Style attribute--> 
</xsl:stylesheet> 

的问题是,当它运行时,它仅匹配无论是在或 之一,我想我错理解如何使用模板中的xsl:copy要求模板为SPAN评估并重新评估其他模板的相同跨度,但我没有成功地使其可以使用或不使用它...

I提前感谢您对此的看法。

此致敬礼。

+1

您可以使用模式使用不同的模板处理相同的节点。在XSLT 2.0及更高版本中,您还有''。 –

+0

Hi Marteen, 对不起,但经过大量的尝试/失败迭代后,我无法使用带有模式的模板对单个跨度中具有多个样式的案例进行递归处理。 有没有人有机会举例说明它的工作原理? 致以问候 – hanoijolly

在这里,您如何与模式做在XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" omit-xml-declaration="yes"/> 

    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'text-decoration:underline;')]" mode="u"> 
     <u> 
      <xsl:apply-templates/> 
     </u> 
    </xsl:template> 

    <xsl:template match="SPAN" mode="u"> 
     <xsl:apply-templates/> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'font-weight: bold;')]" mode="b"> 
     <b> 
      <xsl:apply-templates select="." mode="u" /> 
     </b> 
    </xsl:template> 

    <xsl:template match="SPAN" mode="b"> 
     <xsl:apply-templates select="." mode="u" /> 
    </xsl:template> 

    <xsl:template match="SPAN"> 
     <p> 
      <xsl:apply-templates select="." mode="b" /> 
     </p> 
    </xsl:template> 
</xsl:stylesheet> 

注意匹配SPAN[contains(...)]模板如何具有更高的优先级比一个只匹配SPAN

因此,您有效地更改了模板。对于SPAN标签,输出一个p节点,然后调用b模板检查是否为粗体。 b模板然后调用u模板检查下划线,然后该模板将处理子项。您可以以类似的方式添加更多模板(因此b调用u,然后u调用ii处理子项)。

编辑:如果你想避免编写两个模板的每个,可以在每次对模板合并成一个,像这样

<xsl:template match="SPAN" mode="b"> 
     <xsl:choose> 
      <xsl:when test="contains(@STYLE, 'font-weight: bold;')"> 
       <b> 
        <xsl:apply-templates select="." mode="u" /> 
       </b> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:apply-templates select="." mode="u" /> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

如果你能使用XSLT 2.0,您可以使用优先级, next-match改为减少模板的数量

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" omit-xml-declaration="yes"/> 

    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="SPAN"> 
     <xsl:apply-templates /> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'text-decoration:underline;')]" priority="8"> 
     <u> 
      <xsl:next-match /> 
     </u> 
    </xsl:template> 

    <xsl:template match="SPAN[contains(@STYLE, 'font-weight: bold;')]" priority="9"> 
     <b> 
      <xsl:next-match /> 
     </b> 
    </xsl:template> 

    <xsl:template match="SPAN" priority="10"> 
     <p> 
      <xsl:next-match /> 
     </p> 
    </xsl:template> 
</xsl:stylesheet> 
+0

您好,非常感谢您对此非常清楚的解释!对于XSLT 1.0版本,有两点质疑我:1)如果我想要做5次样式转换而不是2次,这意味着在每个样式中我必须调用其他4个样式?我猜是这样,但是这引起了我的疑问2)如果U情况下调用模板B并且B模板调用模板U我怎么不在调用中有一个“永久循环”?即使解决方案的工作原理,我很想了解... – hanoijolly

+0

在回答你的问题时,模式“b”模板将同一个SPAN元素的模式称为“u”模式。但在这种情况下,“u”模板是“终点”,然后处理子节点,而不是SPAN本身,从而避免递归。 –

+0

如果你还想要一个“我”模板,你可以改变“u”模板来调用一个新的模板“i”模式,然后模式“我”模板将做子处理。 –