在XSL 1.0中防止2 ^ n - 1个条件

时间:2012-03-22 21:36:23

标签: xml xslt xml-parsing xslt-1.0

给出以下XML:

<root>
    Pacman <format bold="1" italic="1">rules</format>!
</root>

什么是比以下更好的实现,这导致2 n -1可能的条件语句?

<xsl:template match="format">
    <xsl:choose>
        <xsl:when test="@bold='1' and @italic='1'">
            <b><i><xsl:value-of-select="."/></i></b>
        </xsl:when>
        <xsl:when test="@bold='1'">
            <b><xsl:value-of-select="."/></b>
        </xsl:when>
        <xsl:when test="@italic='1'">
            <i><xsl:value-of-select="."/></i>
        </xsl:when>
    </xsl:choose>
</xsl:template>

如果我想添加一个新的可能属性,例如underline="1",这会产生一个很大的问题,这将导致4个新的条件。

编辑:还假设我无法使用CSS类,并且必须使用HTML标记进行样式设置。

4 个答案:

答案 0 :(得分:3)

我的XSLT非常生疏,铰链不会让步,但我认为你可以使用<xsl:call-template … />一次处理一个属性,每个属性使用一个模板。

以下可能有一些非常明显的错误,但希望它能给你带来震撼。

<xsl:template name="bold">
    <xsl:choose>
        <xsl:when test="@bold='1'">
            <b><xsl:call-template name="italics" /></b>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="italics" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="italics">
    …
</xsl:template>

答案 1 :(得分:1)

模板可以链接的方式,每个模板在运行时只调用一次,在XSLT中调用两次。

<xsl:template name="bold">
    <xsl:choose>
        <xsl:when test="@bold='1'">
            <b><xsl:call-template name="italics" /></b>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="italics" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="italics">
        <xsl:when test="@italics='1'">
            <i><xsl:call-template name="underscore" /></i>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="underscore" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="underscore">
        <xsl:when test="@underscore='1'">
            ...
        </xsl:when>
        <xsl:otherwise>
            ...
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

答案 2 :(得分:1)

我将从XSLT 2.0解决方案开始,然后告诉您如何将其转换为XSLT 1.0。

<xsl:template match="format[@italic='1']" priority="10">
  <i><xsl:next-match/></i>
</xsl:template>

<xsl:template match="format[@bold='1']" priority="9">
  <b><xsl:next-match/></b>
</xsl:template>

<xsl:template match="format[@underline='1']" priority="8">
  <u><xsl:next-match/></u>
</xsl:template>

<xsl:template match="format" priority="7">
  <xsl:value-of select="."/>
</xsl:template>

现在,xsl:next-match需要XSLT 2.0,但1.0有xsl:apply-imports,它执行几乎相同的工作,除了四个模板规则现在需要在单独的模块中,每个模块都导入下一个模块。不方便,但这就是为什么人们更喜欢2.0。

答案 3 :(得分:0)

当前接受的解决方案以固定的硬编码顺序生成所需属性,该顺序与源XML文档中包含的format元素的属性顺序不对应

在HTML的情况下,这样的解决方案可能是可接受的,但是可能存在其他情况,其中需要保留属性的顺序。

此答案提供保留属性顺序的解决方案。此外,属性的名称​​不硬编码到代码中(可以包含在单独的文档中),这使得代码完全独立于源属性名称或相应名称的任何更改要生成的元素。

这是一个简单的XSLT 1.0解决方案,它占用一个xslt样式表而不使用<xsl:apply-imports>

此解决方案不依赖或了解属性的顺序或其编号,并且如果以任何方式更改此顺序或数量的属性,则可正常工作:

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

 <my:mapping>
   <map old="strikeout">strike</map>
 </my:mapping>

 <xsl:variable name="vMap" select="document('')/*/my:mapping/*"/>

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

 <xsl:template match="format">
   <xsl:variable name="vAttribs" select="@*"/>
   <xsl:call-template name="genAttribs">
     <xsl:with-param name="pAttribs" select="$vAttribs"/>
   </xsl:call-template>
 </xsl:template>

 <xsl:template name="genAttribs">
   <xsl:param name="pAttribs" select="/.."/>

   <xsl:choose>
     <xsl:when test="$pAttribs">
      <xsl:variable name="vMappedElemName"
        select="$vMap[@old = name($pAttribs)]"/>


      <xsl:variable name="vElemName" select=
      "concat($vMappedElemName,
              substring(name($pAttribs[not($vMappedElemName)])
                        ,1,1)
             )
      "/>

        <xsl:element name="{$vElemName}">
            <xsl:call-template name="genAttribs">
              <xsl:with-param name="pAttribs"
                   select="$pAttribs[position() > 1]"/>
            </xsl:call-template>
        </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="."/>
    </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于以下XML文档

<root>
  Pacman
    <format bold="1" italic="1"
    underscore="1"
    strikeout="1">rules</format>!
</root>

产生了想要的正确结果

<root>
  Pacman
    <b>
      <i>
         <u>
            <strike>rules</strike>
         </u>
      </i>
   </b>!
</root>

<强>解释

  1. 逐个处理属性以生成嵌套元素。只有在处理完最后一个属性之后,我们才会在生成的最内层元素的主体中生成属性父级的文本节点

  2. 我们维护一个映射表 - 只有当属性名称到元素名称的所需转换不仅仅是属性名称的第一个字母时,才需要指定映射attribute-name --> element-name。如果在映射表中指定了属性名称,那么我们使用映射表的此元素的字符串值来生成元素名称。

  3. 如果未在映射表中指定属性名称,则对于元素名称,我们使用属性名称的第一个字母。

  4. 因此,如果指定了任何新属性并且其相应(待生成)元素的名称是此属性名称的第一个字母,则解决方案不需要任何修改。 / p>

    最后:请注意,映射表不需要是XSLT代码的一部分(这只是为了方便) - 在realworld中,这将是一个单独的XML文档当需要添加新的attribute-name --> element-name映射时,(文件)和 XSLT代码将不得不更新