Неоднозначное совпадение правил при замене атрибутов style = в XHTML через XSLT - PullRequest
3 голосов
/ 05 февраля 2011

Это продолжение этого вопроса .

У меня есть несколько <span> тегов в документе с несколькими разделенными точкой с запятой атрибутами стиля. Прямо сейчас у меня есть 3 специфических атрибута стиля, которые я хочу перевести в теги. Все работает хорошо в приведенном выше примере, если атрибут style содержит только один из трех атрибутов style. Если диапазон имеет больше, я получаю неоднозначное совпадение правил.

Три атрибута стиля, которые я ищу, это font-style:italic, font-weight:600 и text-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, все будет работать как положено. Должен быть более простой способ, чем дважды выполнить его через преобразование. Есть идеи?


Как предложили Алехандро и Томалак, замена атрибутов style атрибутом class для классов CSS также является опцией.

Ответы [ 2 ]

2 голосов
/ 05 февраля 2011

РЕДАКТИРОВАТЬ : На случай, если настоящая проблема скрывается, я упростил таблицу стилей:

<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>

Примечание : упрощенный анализ с нормализацией пространства для сопоставления свойств в экзистенциальном сравнении.Создание контента без оптимизации (выбор не соответствует, выбор соответствия).Шаблон с именем Stateful или stackful для вывода вложенных элементов .В любом случае есть два правила (удостоверение и span с перезаписью @style) и два шаблона имен (анализатор / токенизатор и генератор вложенного содержимого)

Исходная таблица стилей:

<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>

Примечание : Рекурсионный рай.Вложенный токенайзер для анализа свойств стиля.«Stateful» шаблон для вложенного контента (и, кстати, свойств соответствия производительности)

1 голос
/ 05 февраля 2011

Вот решение XSLT 1.0, использующее шаблон / функцию str-split-to-words FXSL :

<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>
...