Главная » XSLT » Пустое пространство

0

Задача

Требуется преобразовать XML в форматированный текст, но из-за странностей в обработке символов пропуска все попытки форматирования терпят неудачу.

Решение

Рассмотрим следующий аннотированный пример XML-документа. Символы ( I (новая строка), ^ (табуляция) и ? (пробел) обозначают текстовые узлы, содер­жащие только пустое пространство, на которые часто не обращают внимания, хотя они тоже могут копироваться в выходной документ:

<review>^     I

^<author>^    I

>> <name>Sal Mangano</name>^  I

>><email>smangano@somewhere.com</email>^     I

^</author>^   I

<title>XSLT Cookbook</title>^ I

<reviewer>^   I

>><name>?<annonymous/>?</name>^       I

>><email>smangano@somewhere.com</email>^     I

>><comment>^ 

Totally awesome. <b>Worth every cent!</b>?<i>Must buy because I ^    I

know the author peronally and he can sure use the money.</i>^ I

>></comment>^ I

^</reviewer>^ I

</review>^   

Слишком много пустого пространства

1.              Воспользуйтесь командой xsl:strip-space, чтобы избавиться от узлов, содержащих только символы пропуска.

Этот элемент верхнего уровня имеет единственный атрибут elements, значением которого является список разделенных пробелами имен эле­ментов, в которых вы хотели бы убрать лишнее пустое пространство. Под «лишним пустым пространством» понимаются текстовые узлы, содержа­щие только символы пропуска. Это означает, что пустое пространство, раз­деляющее слова в элементе comment выше, не лишнее, так как в нем есть не только символы пропуска. С другой стороны, символы пропуска, обо­значенные специальными символами, – лишнее пустое пространство. Широко распространена следующая идиома: в начале задается команда <xsl:strip-space elements="*"/>, чтобы по умолчанию убрать пустое пространство из всех элементов, а затем с помощью команды xsl:preserve- space перечисляют элементы, для которых этого делать не надо. В XSLT 2.0 разрешено также задавать атрибут elements в виде "*:Name". Это означает, что процессор должен убрать пустое пространство из всех элементов с данным локальным именем независимо от пространства имен.

2.              Воспользуйтесь функцией normalize-space, чтобы избавиться от лиш­него пустого пространства.

Типичная ошибка – предполагать, что команда xsl:strip-space позаботится обо всем «лишнем» пустом пространстве, включая и то, что использовалось для выравнивания текста в элементе comment. Это совсем не так. Анализатор всегда считает, что в тексте, где символы пропуска встречаются наряду с другими, всякое пустое пространство значимо. Что­бы удалить лишнее, применяйте функцию normalize-space, например: <xsl:value-of select="normalize-space(comment)"/>.

3.              Воспользуйтесь для удаления всех символов пропуска функцией translate.

Еще одна распространенная ошибка – предполагать, что функция normalize-space удаляет все символы пропуска. Вовсе нет, она лишь убирает такие символы в начале и в конце, а вместо подряд идущих симво­лов пропуска оставляет один пробел. Если нужно удалить все вообще сим­волы пропуска, применяйте функцию translate, например: translate(something,’&#x20;&#xa;&#xd; &#x9;’, "").

4.              Применяйте элемент xsl:text, если нужно, чтобы завершающие симво­лы пропуска в таблице стилей не считались значимыми.

Обычно элемент xsl:text считается способом сохранить пустое про­странство. Но если разместить такие элементы в стратегически важных точках, то они могут воспрепятствовать интерпретации завершающих сим­волов пропуска как значимых. Сравним результаты применения таблицы стилей из примера 7.2 к документу из примера 7.1 в двух режимах:

Пример 7.1. Входной документ

<numbers>

<number>10</number> <number>3.5</number> <number>4.4 4</number> <number>77.777 7</number> </numbers>

Пример 7.2. Обработка чисел с применением и без применения элемента xsl:text

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/ Transform">

<xsl:output method="text"/> <xsl:strip-space elements="*"/>

<xsl:template match="numbers"> Без пустого тeкcтoвoгo элeмeнтa: <xsl:apply-templates mode="without"/> С пустым текстовым элементом: <xsl:apply-templates mode="with"/>

</xsl:template>

<xsl:template match="number" mode="without">

<xsl:value-of select="."/>, </xsl:template>

<xsl:template match="number" mode="with">

<xsl:value-of select="."/>,<xsl:text/> </xsl:template>

</xsl:stylesheet>

Пример 7.3. Результат

Без пустого текстового элемента: 10, 3.5, 4.44, 77.7777,

С пустым текстовым элементом: 10,3.5,4.44,77.7777,

Отметим, что ничего сверхъестественного в таком применении xsl:text нет. Такой же результат получится, если заменить <xsl:text/> на <xsl:if test="0"/> (но не поступайте так, если только вам не доставляет удовольствия ставить других в тупик). Смысл в том, чтобы поместить узел элемента между запя­той и завершающим символом новой строки. Это порождает узел, содержащий только пустое пространство, который будет проигнорирован. Некоторым, впрочем, и такая запись кажется неочевидной, поэтому можете написать <xsl: text>,</ xsl:text>, если вам так больше нравится.

Слишком мало пустого пространства

1.              Применяйте команду xsl:preserve-space, чтобы отменить действие xsl:strip-space для некоторых элементов.

Не имеет смысла употреблять команду xsl:preserve-space без xsl:strip-space. Ведь по умолчанию все символы пропуска во входном документе и в документах, загруженных с помощью функции document(), и так сохраняются

Имейте в виду, что процессор MSXML по умолчанию удаляет текстовые узлы, не содержащие ничего, кроме символов пропуска. В этом случае можно воспользоваться командой xsl:preserve-space, чтобы испра­вить указанное расхождение со спецификацией.

2.              Используйте команду xsl:text для точного задания промежутков в вы­ходном тексте.

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text"/>

<xsl:strip-space elements="*"/>

<xsl:template match="number"> <xsl:value-of select="."/> <xsl:text>&#xa;</xsl:text>

</xsl:template>

</xsl:stylesheet>

На некоторых платформах (например, Microsoft) разрыв строки обознача­ется двумя символами: возврат каретки и перевод строки. Но, поскольку анализатор XML обязан преобразовывать пару таких символов в один сим­вол новой строки, то не существует способа создать платформенно-незави- симую таблицу стилей. К счастью, в большинстве редакторов для Windows и в утилите командной строки одиночные символы новой строки обраба­тываются правильно. Единственное исключение составляет редактор Notepad, входящий в состав дистрибутива Windows.

3. Употребляйте символы неразрываемого пробела.

В XSLT символ #xA0; (неразрываемый пробел) трактуется не так, как обычные символы пропуска. В частности, и команда xsl:strip-space, и функция normalize- space() его игнорируют. Если в большинстве случаев пустое пространство вам не нужно, но имеются немногочисленные исключения, то можно использовать этот символ во входном документе. Особенно полезен неразрываемый пробел для форматирования HTML-документов, в других контекстах он применяется редко (зависит от программы, которая отображает результат).

Обсуждение

В разделе «Решение» описаны различные приемы управления пустым про­странством. Однако полезно также знать формальные правила XSLT, лежащие в основе этих приемов.

Самые важные правила относятся и к таблице стилей, и к документам:

1.              Текстовый узел не удаляется, если содержит что-либо, кроме символов пропуска (#x20, #x9, #xD или #xA).

Хотя атрибут xml:space встречается в таблицах стилей и входных доку­ментах не так уж часто, нужно понимать, для чего он предназначен.

2.              Если для какого-нибудь элемента-предка текстового узла задан атрибут xml:space со значением preserve, и не существует более близкого пред­ка, у которого атрибут xml:space равен default, то этот текстовый узел не удаляется, даже если содержит только пустое пространство.

Теперь мы рассмотрим правила, применяемые отдельно к таблицам стилей и к документам. Что касается таблиц стилей, то вариантов не так уж много. 3. Единственный элемент таблицы стилей, для которого узлы, содержащие только пустое пространство, по умолчанию сохраняются, – это xsl:text. Здесь «по умолчанию» означает, что противное не задано с помощью команды xml:space="preserve", как описано в п. 2 выше. См. примеры 7.4 и 7.5.

Пример 7.4. Таблица стилей для демонстрации эффекта xsl:text и xml:space="preserve"

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text"/>

<xsl:strip-space elements="*"/>

<xsl:template match="numbers"> Без xml:space="preserve":

<xsl:apply-templates mode="without-preserve"/> С xml:space="preserve":

<xsl:apply-templates mode="with-preserve"/> </xsl:template>

<xsl:template match="number" mode="without-preserve">

<xsl:value-of select="."/><xsl:text> </xsl:text> </xsl:template>

<xsl:template match="number" mode="with-preserve" xml:space="preserve">

<xsl:value-of select="."/><xsl:text> </xsl:text> </xsl:template>

</xsl:stylesheet>

Пример 7.5. Результат

Без xml:space="preserve": 10 3.5 4.44 77.7777 С xml:space="preserve":

10

3.5

4.44

77.7777

В первом случае единственное пустое пространство в выходном документе – пробелы внутри элемента xsl:text. Но во втором шаблоне задан атрибут xml:space="preserve", и потому сохранены все символы пропуска, вклю­чая и оба разрыва строк (первый после <xsl:template …>, второй после </xsl:text>).

К входным документам применяются следующие правила:

?               Первоначально в список элементов, для которых сохраняется пустое про­странство, входят все вообще элементы документа.

?               Если имя элемента удовлетворяет условию NameTest, заданному в коман­де xsl:strip-space, то он удаляется из списка.

?               Если имя элемента удовлетворяет условию NameTest, заданному в коман­де xsl:preserve-space, то он добавляется в список.

Условие NameTest – это либо простое имя (например, doc), либо имя с пре­фиксом пространства имен (например, my:doc), либо метасимвол (например, * ), либо метасимвол с префиксом пространства имен (например, my:*). В XSLT 2.0 разрешена также конструкция *:doc. Если между командами xml:strip- space и xml:preserve-space возникают конфликты, то для их разрешения применяются стандартные правила учета приоритета и предшествования при импорте:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://www.ora.com/XSLTCookbook/ns/my">

<!– Удалить пустое пространство из всех элементов –> <xsl:strip-space="*"/>

<!– кроме входящих в пространство имен "my" –> <xsl:preserve-space="my:*"/>

<!– и имеющих имя foo –> <xsl:preserve-space="foo"/>

См. также

Одно из самых полных обсуждений пустого пространства вы найдете в книге Майкла Кэя XSLT Programmer’s Reference (Wrox, 2001). Там же подробно рассмат­риваются правила предшествования при импорте.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

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