Преобразование содержимого узла для удаления пробелов

#xslt #xslt-1.0

#xslt #xslt-1.0

Вопрос:

Если содержимое citations узла выглядит примерно так:

                 <p>

            WAJWAJADS:

            </p>

<p>

            asdf

            </p>

<p>

            ALSOAS:

            </p>

<p>

            lorem ipsum...<br />
lorem<br />
blah blah <i>

            adfas amp;amp; dasdsaafs

            </i>, April 2011.<br />
lorem lorem dear lord the whitespace

            </p>
  

Есть ли какой-либо способ преобразовать это в правильно отформатированный HTML с помощью XSLT?

normalize-space() просто объединяет все вместе. Лучшее, что мне удалось сделать, это normalize-space() для всех p потомков в for-each цикле и обернуть их в p элемент. Однако тогда все внутренние теги все равно теряются.

Есть ли лучший способ проанализировать это сгенерированное WYSIWYG крушение поезда? К сожалению, я не могу контролировать сгенерированный XML.

Комментарии:

1. Каково ваше определение «правильно отформатированного HTML»?

2. Нет дополнительных пробелов между началом / концом узла и текстом.

Ответ №1:

Я немного изменил ответ Мартина Хоннена:

 <xsl:template match="text()">
    <xsl:value-of select="normalize-space(.)"/>
    <xsl:if test="substring(., string-length(.)) = ' ' and substring(., string-length(.) - 1, string-length(.)) != '  '">
        <xsl:text> </xsl:text>
    </xsl:if>
</xsl:template>
  

он проверяет, является ли последний символ пробелом, а последние 2 символа не являются пробелами, если true, он вставляет пробел.

Ответ №2:

Сначала вам нужно иметь правильно сформированный XML-файл с корнем.

Предполагая, что у вас это есть, вы можете применить преобразование идентификатора для копирования исходного дерева в результат, разделять пробелы между тегами, при необходимости генерировать выходные данные в формате HTML (без объявления XML) с отступом и использовать normalize-space() только в текстовых узлах.

Попробуйте эту таблицу стилей:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes" method="html"/>

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

    <xsl:template match="text()">
         <xsl:value-of select="normalize-space(.)"/>
    </xsl:template>

</xsl:stylesheet>
  

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

 <p>WAJWAJADS:</p>
<p>asdf</p>
<p>ALSOAS:</p>
<p>lorem ipsum...<br>lorem<br>blah blah<i>adfas amp;amp; dasdsaafs</i>, April 2011.<br>lorem lorem dear lord the whitespace
</p>
  

Вы можете увидеть результат, примененный к вашему примеру в этом скрипте XSLT

ОБНОВЛЕНИЕ 1: чтобы добавить дополнительный пробел вокруг каждого текстового узла (и избежать конкатенации при вычислении строкового значения узла), вы можете заменить последний шаблон на:

 <xsl:template match="text()">
    <xsl:value-of select="concat(' ',normalize-space(.),' ')"/>
</xsl:template>
  

Результат:

 <html>
   <p> WAJWAJADS: </p>
   <p> asdf </p>
   <p> ALSOAS: </p>
   <p> lorem ipsum... <br> lorem <br> blah blah <i> adfas amp;amp; dasdsaafs </i> , April 2011. <br> lorem lorem dear lord the whitespace 
   </p>
</html>
  

См.: http://xsltransform.net/3NzcBsE/1

ОБНОВЛЕНИЕ 2: добавление пробела или новой строки после каждого скопированного элемента. Поместите это <xsl:text>amp;#xa;</xsl:text> (для новой строки) или это <xsl:text> </xsl:text> (для пробела) после </xsl:copy> в первом шаблоне:

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

Результат:

 <html>
   <p>WAJWAJADS:</p>

   <p>asdf</p>

   <p>ALSOAS:</p>

   <p>lorem ipsum...<br>
      lorem<br>
      blah blah<i>adfas amp;amp; dasdsaafs</i>
      , April 2011.<br>
      lorem lorem dear lord the whitespace
   </p>

</html>
  

См.: http://xsltransform.net/3NzcBsE/2

Комментарии:

1. Не могли бы вы объяснить значение @*|node() в своем ответе?

2. То @ же attribute:: самое, что и , что означает ось атрибутов. @* или attribute::* означает любой атрибут. node() соответствует любому узлу, кроме атрибутов и пространств имен (он соответствует элементам, текстовым узлам, комментариям и инструкциям по обработке). | Это оператор объединения. Конечное выражение соответствует атрибутам или любому тексту, комментарию, инструкции по обработке или элементу.

3. <xsl:copy> Просто копирует соответствующий узел. <xsl:apply-templates/> Внутренняя <xsl:copy> часть также должна быть явно настроена на выбор атрибутов, поскольку по умолчанию они не выбираются.

4. Как вы можете видеть в своей скрипке, проблема связана с внутренними элементами, такими как i . Из normalize-space() -за текстового содержимого эти элементы смешиваются вместе с родительским текстом.

5. Это можно компенсировать добавлением пробела перед и после каждого текстового узла: <xsl:value-of select="concat(' ',normalize-space(.),' ')"/> . См.: xsltransform.net/3NzcBsE/1

Ответ №3:

Используйте шаблон преобразования идентификатора плюс шаблон для текстовых узлов, выполняющих нормализацию пространства:

<xsl:template match="text()"><xsl:value-of select="normalize-space()"/></xsl:template>

Ответ №4:

Этот вопрос было бы намного проще понять, если бы пример содержал реальный текст вместо тарабарщины. «Нет дополнительных пробелов между началом / концом узла и текстом». недостаточно точное описание ожидаемого результата.

Я собираюсь сделать предположение здесь и предположить, что вы действительно хотите выполнить операцию «от пробелов до одного пробела» для всех текстовых узлов. Это можно сделать следующим образом:

XSLT 1.0

 <xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="text()" priority="1">
    <xsl:variable name="temp" select="normalize-space(concat('x', ., 'x'))" />
    <xsl:value-of select="substring($temp, 2, string-length($temp) - 2)"/>
</xsl:template>

</xsl:stylesheet>
  

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

 <chapter>


           <p>

    This         question          would         have

been       a     lot    <b>   easier      </b>      to understand 

        if     the      example   contained     

   <i>     real  </i>    text    instead   of 

   gibberish.

                     </p>


    <p>

    Here     is       an      example       of     preserving   zero     spaces 

    between    text   nodes:<br/>(continued)       on   a new   line. 




    </p>


        <p>

    Here  is       another      example       of     

    preserving   zero     spaces     within    a      text

    node:     <i>some     text  in      italic</i>       followed    

    by   normal      text. 


    </p>


</chapter>
  

результатом будет:

 <?xml version="1.0" encoding="UTF-8"?>
<chapter>
   <p> This question would have been a lot <b> easier </b> to understand if the example contained <i> real </i> text instead of gibberish. </p>
   <p> Here is an example of preserving zero spaces between text nodes:<br/>(continued) on a new line. </p>
   <p> Here is another example of preserving zero spaces within a text node: <i>some text in italic</i> followed by normal text. </p>
</chapter>
  


Обратите внимание, что при отображении в HTML не будет разницы между вводом и выводом.