【问题标题】:Ambiguous rule match in replacing style= attributes in XHTML via XSLT通过 XSLT 替换 XHTML 中的 style= 属性时的模棱两可的规则匹配
【发布时间】:2011-06-21 16:34:54
【问题描述】:

这是this question的后续。

我在一个文档中有几个<span> 标记,其中有几个分号分隔的样式属性。现在我有 3 个特定的样式属性,我正在寻找将其转换为标签。只要样式属性仅包含三个样式属性之一,在上面的示例中都可以正常工作。如果跨度有更多,我会得到一个模棱两可的规则匹配。

我要查找的三个样式属性是font-style:italicfont-weight:600text-decoration:underline,应将它们从样式属性中移除并分别转换为<em><strong><u> .

这是我当前的 XSLT:

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

<xsl:template match="span[
    contains(translate(@style, ' ', ''), 'font-style:italic')
    ]">
    <xsl:copy>
       <xsl:attribute name="style">
            <xsl:value-of select="substring-before(@style, ' font-style')"/>
            <xsl:value-of select="substring-after(@style, 'italic;')"/>
        </xsl:attribute>
        <em>
            <xsl:apply-templates select="node()"/>
        </em>
    </xsl:copy>
</xsl:template>
<xsl:template match="span[
    contains(translate(@style, ' ', ''), 'font-weight:600')
    ]">
    <xsl:copy>
        <xsl:attribute name="style">
            <xsl:value-of select="substring-before(@style, ' font-weight')"/>
            <xsl:value-of select="substring-after(@style, '600;')"/>
        </xsl:attribute>
        <strong>
            <xsl:apply-templates select="node()"/>
        </strong>
    </xsl:copy>
</xsl:template>

<xsl:template match="span[ 
    contains(translate(@style, ' ', ''), 'text-decoration:underline')
    ]">
    <xsl:copy>
        <xsl:attribute name="style">
            <xsl:value-of select="substring-before(@style, ' text-decoration')"/>
            <xsl:value-of select="substring-after(@style, 'underline;')"/>
        </xsl:attribute>
        <u>
            <xsl:apply-templates select="node()"/>
        </u>
    </xsl:copy>
</xsl:template>

这将生成不明确的规则警告在包含多个所列属性的某些元素上无法正常工作。

输入示例:

<span style=" text-decoration: underline; font-weight:600; color:#555555">some text</span>

转化为:

<span style=" font-weight:600; color:#555555"><u>some text</u></span>

当期望的结果是:

<span style="color:#555555"><b><u>some text</u></b></span>

如何解决此问题的不明确规则匹配?

提前致谢


更新:

如果我将每个模板上的 priorty 设置为降序值,并在第一次 XSLT 运行的输出上再次运行 XSLT,一切都会按预期工作。必须有一种比通过两次转换更简单的方法。有什么想法吗?


正如 Alejandro 和 Tomalak 所建议的,将 CSS 类的 style 属性替换为 class 属性也是一种选择。

【问题讨论】:

  • 好问题,+1。有关完整的 XSLT 1.0 解决方案,请参阅我的答案。 :)
  • 我想知道:您不应该将style 翻译成一个CSS 类(或几个CSS 类),而不是&lt;u&gt;&lt;b&gt; 元素吗?
  • +1 我很高兴回答这个问题,除了我和 Tomalak 一起讨论使用 CSS 样式表而不是内联 @style 和语义标记而不是样式标记。

标签: xslt xhtml transform


【解决方案1】:

编辑:以防真正的问题被隐藏,我简化了样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:s="styles"
 exclude-result-prefixes="s msxsl">
    <s:s prop="font-style:italic" name="em"/>
    <s:s prop="font-weight:600" name="strong"/>
    <s:s prop="text-decoration:underline" name="u"/>
    <xsl:variable name="vStyles" select="document('')/*/s:s"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="span[@style]">
        <xsl:variable name="vrtfProp">
            <xsl:call-template name="parser"/>
        </xsl:variable>
        <xsl:variable name="vProp"
                      select="msxsl:node-set($vrtfProp)/*"/>
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='style']"/>
            <xsl:attribute name="style">
                <xsl:for-each select="$vProp[not(.=$vStyles/@prop)]">
                    <xsl:value-of select="concat(.,';')"/>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:call-template name="generate">
                <xsl:with-param
                     name="pElements"
                     select="$vStyles[@prop=$vProp]/@name"/>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
    <xsl:template name="generate">
        <xsl:param name="pElements" select="/.."/>
        <xsl:choose>
            <xsl:when test="$pElements">
                <xsl:element name="{$pElements[1]}">
                    <xsl:call-template name="generate">
                        <xsl:with-param
                             name="pElements"
                             select="$pElements[position()>1]"/>
                    </xsl:call-template>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="parser">
        <xsl:param name="pString" select="concat(@style,';')"/>
        <xsl:if test="contains($pString,';')">
            <xsl:variable
                 name="vProp"
                 select="substring-before($pString,';')"/>
            <prop>
                <xsl:value-of
                     select="concat(
                                normalize-space(
                                   substring-before($vProp,':')
                                ),
                                ':',
                                normalize-space(
                                   substring-after($vProp,':')
                                )
                             )"/>
            </prop>
            <xsl:call-template name="parser">
                <xsl:with-param
                     name="pString"
                     select="substring-after($pString,';')"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

输出:

<span style="color:#555555;"><strong><u>some text</u></strong></span>

注意:使用空间归一化进行更简单的解析以匹配存在比较中的属性。生成没有优化的内容(选择不匹配,选择匹配)。用于输出嵌套元素的“有状态”或“堆栈”命名模板。无论哪种方式,都有两个规则(身份和@style 覆盖它的span)和两个名称模板(解析器/标记器和嵌套内容生成器)

原始样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:s="styles"
 xmlns:t="tokenizer"
 exclude-result-prefixes="s t msxsl">
    <s:s r="font-style" v="italic" e="em"/>
    <s:s r="font-weight" v="600" e="strong"/>
    <s:s r="text-decoration" v="underline" e="u"/>
    <t:t s=";" n="p"/>
    <t:t s=":" n="t"/>
    <xsl:variable name="vStyles" select="document('')/*/s:s"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="span[@style]">
        <xsl:variable name="vrtfStyles">
            <xsl:call-template name="tokenizer"/>
        </xsl:variable>
        <xsl:copy>
            <xsl:apply-templates select="@*[name()!='style']"/>
            <xsl:call-template name="generate">
                <xsl:with-param name="pStyles"
                                select="msxsl:node-set($vrtfStyles)/*"/>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
    <xsl:template name="generate">
        <xsl:param name="pStyles" select="/.."/>
        <xsl:param name="pAttributes" select="/.."/>
        <xsl:param name="pElements" select="/.."/>
        <xsl:choose>
            <xsl:when test="$pStyles">
                <xsl:variable name="vMatch"
                              select="$vStyles[@r=$pStyles[1]/t[1]]
                                              [@v=$pStyles[1]/t[2]]"/>
                <xsl:call-template name="generate">
                    <xsl:with-param name="pStyles"
                                    select="$pStyles[position()>1]"/>
                    <xsl:with-param name="pAttributes"
                                    select="$pAttributes|
                                            $pStyles[1][not($vMatch)]"/>
                    <xsl:with-param name="pElements"
                                    select="$pElements|$vMatch"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="$pAttributes">
                <xsl:attribute name="style">
                    <xsl:for-each select="$pAttributes">
                        <xsl:value-of select="concat(t[1],':',t[2],';')"/>
                    </xsl:for-each>
                </xsl:attribute>
                <xsl:call-template name="generate">
                    <xsl:with-param name="pElements" select="$pElements"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="$pElements">
                <xsl:element name="{$pElements[1]/@e}">
                    <xsl:call-template name="generate">
                        <xsl:with-param name="pElements"
                                        select="$pElements[position()>1]"/>
                    </xsl:call-template>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="tokenizer">
        <xsl:param name="pTokenizer" select="document('')/*/t:t"/>
        <xsl:param name="pString" select="@style"/>
        <xsl:choose>
            <xsl:when test="not($pTokenizer)">
                <xsl:value-of select="normalize-space($pString)"/>
            </xsl:when>
            <xsl:when test="contains($pString,$pTokenizer[1]/@s)">
                <xsl:call-template name="tokenizer">
                    <xsl:with-param name="pTokenizer" select="$pTokenizer"/>
                    <xsl:with-param name="pString"
                                    select="substring-before(
                                               $pString,
                                               $pTokenizer[1]/@s
                                            )"/>
                </xsl:call-template>
                <xsl:call-template name="tokenizer">
                    <xsl:with-param name="pTokenizer" select="$pTokenizer"/>
                    <xsl:with-param name="pString"
                                    select="substring-after(
                                               $pString,
                                               $pTokenizer[1]/@s
                                            )"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="{$pTokenizer[1]/@n}">
                    <xsl:call-template name="tokenizer">
                        <xsl:with-param name="pTokenizer"
                                        select="$pTokenizer[position()>1]"/>
                        <xsl:with-param name="pString" select="$pString"/>
                    </xsl:call-template>
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

注意:递归天堂。用于解析样式属性的嵌套标记器。嵌套内容的“有状态”模板(顺便说一下,还有性能匹配属性)

【讨论】:

  • +1 表示努力。现在我意识到拥有像 FXSL 这样的库是多么强大。 :)
【解决方案2】:

这是一个使用str-split-to-words模板/函数FXSL的XSLT 1.0解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext"
>
  <xsl:import href="strSplit-to-Words.xsl"/>
  <xsl:output indent="yes" omit-xml-declaration="yes"/>

  <xsl:param name="pStyleReps">
    <r s="font-style:italic"><em/></r>
    <r s="font-weight:600"><strong/></r>
    <r s="text-decoration:underline"><u/></r>
  </xsl:param>

  <xsl:variable name="vReps" select=
  "document('')/*/xsl:param[@name='pStyleReps']/*"/>

   <xsl:template match="span">
       <xsl:variable name="vrtfStyles">
         <xsl:call-template name="str-split-to-words">
          <xsl:with-param name="pStr" select="@style"/>
          <xsl:with-param name="pDelimiters" select="';'"/>
         </xsl:call-template>
       </xsl:variable>

       <xsl:variable name="vStyles" select=
       "ext:node-set($vrtfStyles)/*"/>

       <xsl:choose>
        <xsl:when test=
         "not($vReps/@s[contains(current()/@style, .)])">
          <xsl:copy-of select="."/>
        </xsl:when>
        <xsl:otherwise>
          <span>
           <xsl:copy-of select="@*"/>
           <xsl:attribute name="style">
             <xsl:for-each select=
              "$vStyles[not(translate(.,' ','')=$vReps/@s)]">
              <xsl:value-of select="."/>
              <xsl:if test="not(position()=last())">;</xsl:if>
             </xsl:for-each>
           </xsl:attribute>

           <xsl:call-template name="styles2markup">
             <xsl:with-param name="pStyles" select=
             "$vReps/@s
                 [contains
                   (translate(current()/@style, ' ', ''),
                    .
                    )
                 ]"/>
           </xsl:call-template>
          </span>
        </xsl:otherwise>
       </xsl:choose>
    </xsl:template>

    <xsl:template name="styles2markup">
     <xsl:param name="pResult" select="text()"/>
     <xsl:param name="pStyles"/>

     <xsl:choose>
     <xsl:when test="not($pStyles)">
      <xsl:copy-of select="$pResult"/>
     </xsl:when>
     <xsl:otherwise>
      <xsl:variable name="vrtfnewResult">
        <xsl:element name="{name($pStyles[1]/../*)}">
         <xsl:copy-of select="$pResult"/>
        </xsl:element>
      </xsl:variable>

      <xsl:call-template name="styles2markup">
       <xsl:with-param name="pStyles" select=
       "$pStyles[position()>1]"/>
       <xsl:with-param name="pResult" select=
       "ext:node-set($vrtfnewResult)/*"/>
      </xsl:call-template>
     </xsl:otherwise>
     </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<span style=" text-decoration: underline; font-weight:600; color:#555555">some text</span>

产生想要的正确结果

<span style=" color:#555555">
   <u>
      <strong>some text</strong>
   </u>
</span>

【讨论】:

    猜你喜欢
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多