2013-04-12 1 views
1

Я работаю с преобразованием XSL, которое применяется к HTML-документу. Соответствующий HTML пример:В XSLT, Замените одно значение класса при наличии нескольких значений

<div class="row home-row"> 
    <p>Hello</p> 
</div> 

Я выбираю элементы, которые соответствуют этим со следующим XSL (который работает хорошо):

<xsl:template match="//*[contains(concat(' ', normalize-space(@class), ' '), ' row ')]" priority="1"> 

Тогда, если условие Я раньше проверил матчи, я заменяю " строка»с„строка-жидкость“, как это:

<xsl:variable name="original-row-class" select="string(@class)" /> 
<xsl:variable name="row-class"> 
        <xsl:call-template name="replace"> 
         <xsl:with-param name="text" select="$original-row-class" />  
         <xsl:with-param name="replace" select="' row '" /> 
         <xsl:with-param name="by" select="' row-fluid '" /> 
        </xsl:call-template> 
       </xsl:variable> 
       <xsl:copy> 
        <xsl:attribute name="class"><xsl:value-of select="$row-class" /></xsl:attribute> 
        <xsl:apply-templates select="@*[local-name() != 'class']|node()[local-name() != 'class']"/> 
       </xsl:copy> 

Это также работает довольно хорошо, за исключением того, что он заменяет все упоминания о„строки“с„строка-жидкость“, даже если они находятся внутри„домашней строки“ , То, что я хотел бы сделать, - это изменить классы «row» на «row-fluid», но при этом игнорировать классы «home-row». Это возможно?

Следует отметить, что я заблокирован XSLT 1.0.

Update: добавление заменить шаблон ниже (надо было думать о том, что раньше!):

<xsl:template name="replace"> 
    <xsl:param name="text" /> 
    <xsl:param name="replace" /> 
    <xsl:param name="by" /> 
    <xsl:choose> 
     <xsl:when test="contains($text, $replace)"> 
     <xsl:value-of select="substring-before($text,$replace)" /> 
     <xsl:value-of select="$by" /> 
     <xsl:call-template name="replace"> 
      <xsl:with-param name="text" select="substring-after($text,$replace)" /> 
      <xsl:with-param name="replace" select="$replace" /> 
      <xsl:with-param name="by" select="$by" /> 
     </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="$text" /> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Спасибо, Jonathan

+0

Можете ли вы использовать шаблон «заменить», который вы вызываете, поскольку проблема действительно находится в коде для этого. Благодаря! –

+0

Конечно, я должен был подумать, что это может быть так. Обновлено. –

+0

Спасибо! Вы уверены, что показываете правильный XSLT? Просто когда вы вызываете шаблон «replace», вы передаете его «row» в качестве параметра с пробелами с обеих сторон, но вы также передаете ему исходное имя класса без пробела в начале.Это означает, что шаблон «replace» не должен на самом деле ничего заменить ... –

ответ

1

Всего несколько штрихов к исходной трансформации и теперь он работает в разыскиваемом пути - не нужны никакие функций расширения:

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

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

<xsl:template match="*[contains(concat(' ', @class, ' '), ' row ')]"> 
    <xsl:variable name="original-row-class" select="string(@class)" /> 
    <xsl:variable name="row-class"> 
    <xsl:call-template name="replace"> 
     <xsl:with-param name="text" select= 
     "concat(' ', $original-row-class, ' ')" /> 
     <xsl:with-param name="replace" select="' row '" /> 
     <xsl:with-param name="by" select="' row-fluid '" /> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:copy> 
    <xsl:attribute name="class"> 
     <xsl:value-of select="normalize-space($row-class)" /> 
    </xsl:attribute> 
    <xsl:apply-templates select= 
     "@*[local-name() != 'class']|node()"/> 
    </xsl:copy> 
</xsl:template> 

    <xsl:template name="replace"> 
     <xsl:param name="text" /> 
     <xsl:param name="replace" /> 
     <xsl:param name="by" /> 
     <xsl:choose> 
      <xsl:when test="contains($text, $replace)"> 
      <xsl:value-of select="substring-before($text,$replace)" /> 
      <xsl:value-of select="$by" /> 
      <xsl:call-template name="replace"> 
       <xsl:with-param name="text" select="substring-after($text,$replace)" /> 
       <xsl:with-param name="replace" select="$replace" /> 
       <xsl:with-param name="by" select="$by" /> 
      </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
      <xsl:value-of select="$text" /> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

При нанесении на предоставленном документе XML:

<div class="row home-row"> 
    <p>Hello</p> 
</div> 

разыскиваемый, правильный результат получается:

<div class="row-fluid home-row"> 
    <p>Hello</p> 
</div> 
+0

Это фантастика. Только то, что я искал. Большое спасибо, Димитрий. –

+0

@ JonathanStegall, добро пожаловать. –

3

Я думаю, вы должны разделить класс строку на слова с повторным вызовом шаблона. e.g. И сравните/замените каждое слово.

ОК и вот решение, основанное на рекурсивном подходе.

 <xsl:template match="test" > 
      <xsl:variable name ="new_class"> 
       <xsl:call-template name="replace_words"> 
        <xsl:with-param name="replace" select="'xx'"/> 
        <xsl:with-param name="by" select="'yyy'"/> 
        <xsl:with-param name="words" select="'xx xx-a xx-b xx xx'"/> 
       </xsl:call-template> 
      </xsl:variable> 
      <xsl:value-of select="$new_class"/> 

     </xsl:template> 


     <xsl:template name="replace_words"> 
      <xsl:param name="replace"/> 
      <xsl:param name="by"/> 
      <xsl:param name="words"/> 
      <xsl:choose> 
       <xsl:when test="contains($words,' ')"> 
        <!-- try replace first word--> 
        <xsl:call-template name="replace_words"> 
         <xsl:with-param name="replace" select="$replace"/> 
         <xsl:with-param name="by" select="$by"/> 
         <xsl:with-param name="words" select="substring-before($words,' ')"/> 
        </xsl:call-template> 
        <!-- dlimeter --> 
        <xsl:text> </xsl:text> 
        <xsl:call-template name="replace_words"> 
         <xsl:with-param name="replace" select="$replace"/> 
         <xsl:with-param name="by" select="$by"/> 
         <xsl:with-param name="words" select="substring-after($words,' ')"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:choose> 
         <xsl:when test="$replace = $words"> 
          <xsl:value-of select="$by"/> 
         </xsl:when> 
         <xsl:otherwise> 
          <xsl:value-of select="$words"/> 
         </xsl:otherwise> 
        </xsl:choose> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:template> 
    </xsl:stylesheet> 
+0

Это кажется убедительным, но я не получаю его правильно. Кажется, просто удалите оба. –

+0

@ Jonathan Stegall рад, что я мог бы помочь. Даже если это был только намек на правильное направление. –

+0

Для решения этой проблемы токенизация вообще не нужна. Существует более прямолинейное и гораздо более эффективное решение. –

0

Вот что, кажется, работает для меня. Пропуск разделительных линий и склеивание всего этого ниже.

В то время как я получил это право перед тем, как был опубликован ответ ниже, я попробовал это, и он не сразу работал. Думаю, в этот момент мне было бы любопытно, есть ли что-то ужасное в этом, но в остальном я буду придерживаться этого. Фантастическая направленность ниже, по крайней мере.

<xsl:template match="//*[contains(concat(' ', normalize-space(@class), ' '), ' row ')]" priority="1"> 

    <xsl:param name="separator" select="' '" /> 

    <xsl:variable name="class-array"> 
     <xsl:attribute name="name"> 
      <xsl:value-of select="$original-row-class"/> 
     </xsl:attribute> 
     <xsl:call-template name="tokenize"> 
      <xsl:with-param name="text" select="$original-row-class" /> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="items" select="ext:node-set($class-array)" /> 

    <xsl:variable name="row-class"> 
     <xsl:for-each select="$items/item"> 
      <xsl:choose> 
      <xsl:when test="position() = 1"> 
       <xsl:choose> 
        <xsl:when test=". = 'row'">row-fluid</xsl:when> 
        <xsl:otherwise><xsl:value-of select="." /></xsl:otherwise> 
       </xsl:choose>  
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:choose> 
        <xsl:when test=". = 'row'"><xsl:value-of select="concat($separator, 'row-fluid') "/></xsl:when> 
        <xsl:otherwise><xsl:value-of select="concat($separator, .) "/></xsl:otherwise> 
       </xsl:choose> 
      </xsl:otherwise> 
      </xsl:choose> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:copy> 
     <xsl:attribute name="class"><xsl:value-of select="$row-class" /></xsl:attribute> 
     <xsl:value-of select="original-row-class" /> 
     <xsl:apply-templates select="@*[local-name() != 'class']|node()[local-name() != 'class']"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template name="tokenize"> 
    <xsl:param name="text" select="."/> 
    <xsl:param name="sep" select="' '"/> 
    <xsl:choose> 
     <xsl:when test="not(contains($text, $sep))"> 
      <item> 
       <xsl:value-of select="normalize-space($text)"/> 
      </item> 
     </xsl:when> 
     <xsl:otherwise> 
      <item> 
       <xsl:value-of select="normalize-space(substring-before($text, $sep))"/> 
      </item> 
      <xsl:call-template name="tokenize"> 
       <xsl:with-param name="text" select="substring-after($text, $sep)"/> 
      </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 
+0

На самом деле, вашему первоначальному преобразованию потребовалось всего несколько касаний, чтобы заставить его работать в нужном направлении - не требуется токенизация и не нужны функции расширения. –

Смежные вопросы