XSLT

Холзнер Стивен

Глава 3

Создание и применение шаблонов

 

 

Эта глава полностью посвящена созданию шаблонов и работе с ними как с основой таблиц стилей XSLT. Каждый шаблон образует правило, которое процессор XSLT пытается применить к исходному документу.

В главе 2 мы кратко рассмотрели таблицы стилей и основные шаблоны, при помощи которых наши примеры таблиц стилей в действительности могли что-то сделать. В этой главе мы собираемся подробно изучить шаблоны, а в следующей мы увидим, какие виды выражений можно применять для создания образцов выбора в шаблонах, позволяющих находить требуемые узлы. Образцы выбора (match pattern) являются подмножеством всего языка XPath и достаточно сложны для того, чтобы посвятить им отдельную главу.

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

 

Создание шаблона

В главе 2 для выбора узлов в planets.xml и преобразования этого документа в HTML я создал основной шаблон. Шаблоны в таблицах стилей создаются при помощи элементов , задающих правила для требуемых преобразований. Мы создали шаблон, находивший корневой элемент по образцу "/PLANETS", которому соответствуют все элементы , дочерние для корневого узла:

 xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">

 

  .

  .

  .

 

.

.

.

Когда процессор XSLT находит узел, удовлетворяющий образцу вашего шаблона, этот узел становится контекстным узлом шаблона, то есть все операции производятся над этим узлом. На текущий узел можно ссылаться при помощи выражения XPath «.». Другие выражения XPath мы рассмотрим в этой главе и в главе 7.

УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

В XSLT 1.0 возникали трудности при выборе элементов или атрибутов с нулевыми значениями, что должно быть исправлено в XSLT 2.0.

Внутри шаблона создайте разметку HTML, начинающую требуемую таблицу, — такая прямая вставка разметки называется элементом буквального (literal) результата. Когда процессор встречает литерал, он копирует его в результирующее дерево:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

    

   

     

     

      

      

      

     

    

Name Mass Radius Day

  

  

 

 .

 .

 .

Однако это правило обрабатывает только элемент , но не его дочерние элементы , содержащие сами данные. В соответствии с правилами шаблона по умолчанию, элементы будут обрабатываться, если установить шаблон для их выбора. Это, кстати, нежелательно, поскольку я хочу вставить результат обработки элементов в определенное место в создаваемом файле HTML. Для этого мне потребуется применить элемент .

 

Обработка дочерних узлов

Элемент дает указание процессору XSLT обрабатывать все совпадающие шаблоны для дочерних узлов контекстного узла. Элемент дает возможность явно указать, когда следует закончить обработку дочерних узлов, а это имеет решающее значение при вставке их данных в нужное место в HTML-таблице.

Один важный момент часто вызывает затруднения: элемент по умолчанию применяет шаблоны только к дочерним узлам контекстного или выбранного узла или набора узлов. Это выглядит довольно безобидно, но многие люди забывают о том, что атрибуты не считаются дочерними узлами элементов и также ими не являются объявления пространств имен. Это значит, что для обработки как элементов, так и атрибутов следует осуществить еще один или два дополнительных шага, что мы увидим далее в этой главе.

В следующем примере мы поместим элемент туда, куда нам требуется вставить данные из элементов в таблицу HTML. Я также добавил новый шаблон для обработки элементов :

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">    

   

   

    

     The Planets Table

    

   

     

     

      

      

      

     

    

   

Name Mass Radius Day

   

  

 

 

  .

  .

  .

 

В новом шаблоне, обрабатывающем элементы , я должен извлечь данные из каждого элемента , то есть восстановить значения в дочерних элементах элемента , таких как , и т.д.:

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 .

 .

 .

Это можно сделать при помощи элемента .

 

Доступ к значениям узлов

Элемент записывает в результирующий документ строковое значение выражения; в частности, с его помощью можно возвратить значение узла, которым для элемента будет заключенный в элемент текст. Атрибуту выбора элемента можно присвоить выражение XPath, задающее узел или набор узлов. В шаблоне, задающем элементы , обратиться к дочернему элементу можно при помощи выражения XPath "child::MASS". Как мы увидим в главе 4, выражения XPath можно писать разными способами: например, вместо "child::MASS" можно просто написать "MASS". Таким образом, получить данные дочерних элементов, таких как , и т.д., можно следующим способом (листинг 3.1).

Листинг 3.1. Полная версия planets.xsl

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

  

    <Н1>

     The Planets Table

    

   

     

     

      

      

      

     

    

    

Name Mass Radius Day

   

  

 

 

  

  

   

   

   

  

 

 

Создание образцов выбора

Как вы могли ожидать, исходя из того, что "child::MASS" можно представить в виде "MASS" и применения шаблонов, таких как "/", "/PLANETS" и т.д., вплотную познакомиться с созданием образцов выбора не так-то просто — этому посвящена вся глава 4.

Образцы выбора (match pattern) являются подмножеством полного языка XPath, их можно использовать в элементах , и . В частности, в образец можно установить атрибут match у и , и атрибуты count и from элемента . В следующем списке приведен ряд примеров образцов выбора, много других примеров будет показано в главе 4 при подробном обсуждении использования XPath для выбора узлов и атрибутов:

• "/" выбирает корневой узел;

• "*" выбирает элементы узлов (но не всех узлов, как зачастую ошибочно полагают);

• "PLANET" выбирает элементы ;

• "PLANET/MASS" выбирает все элементы , дочерние для элемента ;

• "//PLANET" выбирает все элементы , производные от корневого узла;

• "." выбирает текущий узел (технически это не образец выбора, а выражение XPath, как мы увидим в главе 7).

Образцы можно также использовать в атрибуте select элементов , , , и ; фактически атрибут select этих элементов может содержать полные выражения XPath, а не только образцы выбора. Атрибут select элемента определяет дочерний узел, значение которого нужно получить:

 

 

  

  

  

 

Теперь пора воспользоваться атрибутом select элемента , поскольку это даст нам возможность указать, какой шаблон использовать и когда.

 

Выбор применяемого шаблона

Вплоть до нынешнего момента я использовал только версию элемента по умолчанию, как, например:

 

 

 

  

  

 

 

Name Mass Radius Day

Простое использование только указывает процессору XSLT осуществлять поиск всех шаблонов, выбирающих дочерние узлы контекстного узла, — это использование по умолчанию. Однако иногда это не лучший вариант, поскольку может потребоваться применять шаблоны в определенном порядке или иным образом выбрать применяемые шаблоны (последнее можно сделать при помощи атрибута select элемента ).

Например, до сих пор мы только получали значение каждого элемента , и при помощи :

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

    </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">    

   

   

   

     The Planets Table

   

   

     

     

      

      

     

     

    

    

Name Mass Radius Day

  

 

 

 

  

  

   

  

  

 

 

Здесь только извлекается «сырое» строковое значение каждого узла, которое помещается в HTML-таблицу. Однако может потребоваться дополнительная обработка каждого элемента — например, получить также значения атрибутов UNITS каждого элемента и отобразить их:

 

  Mercury .0553

  58.65

  1516

  .983

  43.4

 

 .

 .

 .

Для этого нельзя просто применить элемент , поскольку он вернет значение узла только как текст, а не как значения атрибута. Вместо этого нужно создать новый набор шаблонов, по одному для каждого интересующего нас элемента: , и . У элемента нет никаких атрибутов, поэтому для него не нужны шаблоны — нужно только значение узла. Каждому из этих новых шаблонов требуется получить значение элемента, а также значение атрибута UNITS.

Чтобы быть уверенным в том, что эти новые шаблоны применяются в правильном порядке, соответствующем заголовкам HTML-таблицы, я явно перечислю все новые шаблоны, выбирая их один за другим при помощи атрибута select:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

    </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

   

   

     

     

     

    

     

    

Name Mass Radius Day

  

 

 

 

  

  

   

   

   

 

 

ИСПОЛЬЗОВАНИЕ ИМЕНОВАННЫХ ШАБЛОНОВ

К шаблонам можно также обращаться по имени. Этот вопрос будет рассмотрен в главе 9.

Теперь к каждому элементу , и применяется новый шаблон: мы не просто получаем строковое значение узла при помощи , мы можем дополнительно обработать каждый элемент — например, прочитать значения атрибута UNITS. Сначала я получу строковые значения каждого из элементов , и . Теперь, когда у нас есть шаблон для каждого из этих узлов-элементов и каждый узел является контекстным узлом в своем шаблоне, вместо того, чтобы ссылаться на узел элемента по имени, мы можем сослаться на него как на контекстный узел при помощи выражения XPath "." (листинг 3.2):

Листинг 3.2. Версия planets.xsl с выбором

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

  

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

    

   

    

     

     

     

     

    

Name Mass Radius Day

   

  

 

 

  

  

   

   

   

 

 

 

 

 

 

  

 

 

 

 

Этот код только воспроизводит то, что мы уже делали ранее в предыдущей версии planets.xsl, использующей , и т.д., то есть читающей и отображающей данные каждого элемента , и . Теперь же, когда у нас есть отдельный шаблон для каждого из этих элементов, можно осуществить дополнительную обработку — например чтение значений атрибутов.

 

Чтение значений атрибутов

Чтобы получить доступ к значению атрибута при помощи XPath, нужно добавить к имени атрибута префикс @, например: "@src", "@height", "@width" и т.д.

Для выбора любого атрибута можно применить выражение "@*". Чтобы сослаться на атрибут UNITS в каждом элементе , и , следует использовать выражение "@UNITS". Следовательно, получить значения и отобразить единицы (unit) каждого измерения в planets.xml можно так (листинг 3.3).

Листинг 3.3. Чтение значений атрибутов

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

   

    

    

     

     

     

     

    

Name Mass Radius Day

   

  

 

 

  

  

   

   

   

  

 

 

  

  

 

 

  

  

 

 

 

  

 

Результаты работы показаны на рис. 3.1, Как видите, теперь мы извлекли строковое значение атрибута UNITS и отобразили его.

Рис. 3.1. Вывод значений атрибутов, первый вариант

Рисунок 3.1 не совсем точен: обратите внимание на то, что между значением и соответствующей единицей измерения в таблице нет пробела. Процессор XSLT просто поместил в результирующее дерево текст без каких-либо разделителей между текстовыми узлами. Хотя это в точности соответствует требованиям рекомендации XSLT, нам бы хотелось, чтобы элементы таблицы выглядели как «1516 miles», а не «1516miles». Как нам добавить этот дополнительный пробел?

 

Элемент <xsl:text>

Работа с пробелами всегда обсуждается при рассмотрении XSLT, и в данной главе я уделю этой теме некоторое время. Вставить единственный пробел несложно при помощи элемента , который используется для вставки символьного текста непосредственно в дерево вывода. У этого элемента только один атрибут:

• disable-output-escaping. Устанавливается в yes для того, чтобы такие символы, как < и >, выводились буквально, а не как < и >. По умолчанию установлен в no.

Этот элемент может содержать только текстовый узел.

Текстовые узлы создаются при помощи элемента , позволяющего по ходу дела замещать элементы целиком на текст. Одна из целей применения — сохранить символы-разделители, как в следующем примере (листинг 3.4), в котором элемент используется для вставки пробелов.

Листинг 3.4. Вставка пробелов в таблицу стилей

 

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

    

    

    

     

     

     

     

    

Name Mass Radius Day

   

  

 

 

  

  

   

   

  

 

 

  

  

  

 

 

  

  

  

 

 

  

 

  

 

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

Рис. 3.2. Вывод значений атрибутов, второй вариант 

Как видите, элемент весьма удобен. Тем не менее вам следует знать вот что: по умолчанию элементы заменяют символы, которые могут входить в разметку, на эскейп-последовательности. Например, Here is а greater-than sign: > будет выведено как «Here is a greater-than sign: >,», а не как «Herе is a greater-than sign: >». А если попытаться использовать < внутри элемента , процессор XSLT посчитает, что вы пытаетесь открыть элемент внутри элемента , что неверно. Как же вывести значащие символы, такие как < и >, если есть необходимость? Это можно сделать путем отключения вывода ESC-последовательностей (disabling output escaping).

УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

Один из вопросов, которые призван решить XSLT 2.0, это упрощение импорта подобного текста без разбора из других файлов.

 

Отключение вывода эскейп-последовательностей

При помощи можно включать в выходной документ непосредственно символы < и &, а не последовательности < и &. Для этого следует установить атрибут disable-output-escaping элемента в yes («да», по умолчанию он установлен в no, «нет»). В приведенном ниже примере я при помощи непосредственно пишу текст "" в выходной документ:

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

 

  

   

     </p> <p class="paragraph">      Planets </p> <p class="paragraph">    

   

   

   

   

  

 

 

 

   <PLANET/>

  

 

Результат следующий:

 

   </p> <p class="paragraph">    Planets </p> <p class="paragraph">  

 

 

  

  

  

 

Разумеется, не обязательно выводить при помощи : этот элемент можно было поместить непосредственно в элемент буквального результата. Но что делать в тех случаях, когда процессор XSLT не сможет распознать нужный нам в выходном документе элемент как фактический элемент? Например, в преобразованных документах XHTML вам потребуется , но процессор XSLT сочтет, что это не хорошо сформированный XML. Как поместить этот элемент в выходные данные?

Можно попробовать поместить этот элемент в раздел , как мы увидим в главе 6, и попытаться трактовать его как простые символьные данные, но процессоры XSLT все равно заменят < на <, а > на >.

Правильный способ добавить в вывод элемент — использовать атрибут doctype-public элемента , как мы увидим в главе 6, но в качестве демонстрации для этих целей здесь я отключу вывод ESC-последовательностей в элементе (хотя этот способ не рекомендован для создания элементов ). Вот как это выглядит:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

 

   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">

  

 

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

   

     The Planets Table

    

   

    

     

     

     

     

    

Name Mass Radius Day

  

  

 

 

  

  

   

   

   

  

 

 

  

  

  

 

 

  

  

  

 

 

  

  

  

 

 

А вот результат:

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

   .

   .

   .

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

 

Написание значений атрибутов

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

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

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

 

  MASS=".0553 (Earth = 1)" NAME="Mercury"/>

 

  MASS=".815 (Earth = 1)" NAME="Venus"/>

 

  MASS="1 (Earth = 1)" NAME="Earth"/>

Чтобы создать преобразование, нельзя просто использовать следующее выражение, в котором я взял значения элементов , и и попытался сделать их значениями атрибутов:

 "

  MASS=""

  DAY=""

/>

Это не будет работать, поскольку нельзя использовать < внутри значений атрибутов, как я сделал в предыдущем примере. В XSLT для этого есть несколько способов. Один из них — использовать шаблоны значений атрибутов. 

 

Шаблоны значений атрибутов

Шаблон имени значения атрибута не имеет ничего общего с теми шаблонами, с которыми мы до сих пор работали, — то есть с шаблонами для создания правил таблицы стилей. Напротив, применение шаблона значения атрибута (attribute value template) означает лишь, что значение атрибута может быть установлено во время выполнения.

В этом случае атрибут можно установить в значение выражения XPath (более подробно эта тема изучается в главе 4), если заключить выражение в фигурные скобки. Например, чтобы установить атрибут NAME в строковое значение элемента , дочернего элемента контекстного узла, это значение можно присвоить следующим образом: NAME={DESCRIPTION}.

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

Листинг 3.5. Работа с шаблонами значений атрибутов

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

   

  

 

 

  

 

Вот как это делается; посмотрите теперь на результирующий документ, в котором значения в различных элементах были преобразованы в атрибуты:

 

 

 

Предположим теперь, что нам также нужно включить все единицы измерения. Каждый элемент , и содержит атрибут UNITS, задающий единицы измерения, и можно извлечь эти значения. Контекстным узлом является элемент , поскольку шаблон установлен для выбора этого элемента, поэтому на дочерние элементы , и можно ссылаться как "MASS", "NAME" и "RADIUS". Для обращения к атрибуту UNITS этих элементов можно использовать синтаксис "MASS/@UNITS", "NAME/@UNITS", и "RADIUS/@UNITS":

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

  

  

 

 

  

   MASS="{MASS} {MASS/@UNITS}"

   RADIUS="{RADIUS} {RADIUS/@UNITS}"

   DAY="{DAY} {DAY/@UNITS}"/>

 

И вот результат, включающий единицы измерения:

 

  MASS=".0553 (Earth = 1)" NAME="Mercury"/>

 

  MASS=".815 (Earth = 1)" NAME="Venus"/>

 

  MASS="1 (Earth = 1)" NAME="Earth"/>

Заметьте, что в шаблонах значений атрибутов нельзя использовать вложенные фигурные скобки, и в выражении, использующем фигурные скобки, — таком как function printHello {cout << 'Hello';} — фигурные скобки необходимо удваивать, для того чтобы процессор XSLT их игнорировал: function printHello {{cout<<'Hello';}}.

Шаблоны значений атрибутов всегда работают с контекстным узлом. Тем не менее, нельзя использовать шаблоны значений атрибутов в произвольном месте таблицы стилей, что зачастую и вызывает затруднения у XSLT-разработчиков. Шаблоны значений атрибутов можно использовать только в следующих местах:

• элементы буквального результата;

• элементы расширения (см. главу 5);

• . Здесь можно использовать атрибуты name и namespace (см. главу 6);

• . Здесь можно использовать атрибуты name и namespace (см. главу 6);

• . Здесь можно использовать атрибуты format, lang, letter-value, grouping-separator и grouping-size (см. главу 4);

• . Здесь можно использовать атрибут name (см. главу 6);

• . Здесь можно использовать атрибуты lang, data-type, order и case-order (см. главу 5).

В главе 6 эта тема рассмотрена более подробно: мы узнаем, как создавать атрибуты (и новые элементы) с нуля. Дополнительная информация об использовании выражений XPath в шаблонах значений атрибутов приведена в главе 7.

 

Обработка символов-разделителей

Поначалу символы-разделители (whitespace) доставляют авторам XSLT много хлопот. В главе 2 объяснялось, что «чистые» узлы-разделители — это текстовые узлы, содержащие только символы-разделители (пробелы, возвраты каретки, переводы строки и символы табуляции). Эти узлы по умолчанию копируются из исходного документа.

Заметьте, что в таблице стилей также могут быть узлы-разделители:

 

 

 

В нашем случае пробелы используются для выравнивания элементов таблицы стилей, а возвраты каретки — для разрежения кода. Чистые узлы-разделители, такие, как этот, не копируются из таблицы стилей в выходной документ. Заметьте, однако, что разделитель из следующего элемента копируется в выходной документ, так как это не чистый узел-разделитель (он также содержит текст «The Planets Table»): </p> <p class="paragraph"> <xsl:template match="/PLANETS"> </p> <p class="paragraph">  <HTML> </p> <p class="paragraph">   <HEAD> </p> <p class="paragraph">    <TITLE> </p> <p class="paragraph">     The Planets Table </p> <p class="paragraph">   

   .

   .

   .

Если вы хотите убрать этот разделитель и сохранить выравнивание, можно использовать пустые элементы , так чтобы символы-разделители стали чистыми узлами-разделителями:

 

  

    </p> <p class="paragraph">     <xsl:text/>The Planets Table<xsl:text/> </p> <p class="paragraph">   

   .

   .

   .

Чистые узлы-разделители не копируются из таблицы стилей в выходной документ, если только они не находятся внутри элемента , или у элемента, в который они вложены, атрибут xml:space не установлен в «preserve» (сохранить).

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

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

  

  

 

и примените ее к planets.xml; все символы-разделители будут также скопированы в результирующий документ:

 

  Mercury

  .0553

  58.65

  1516

     43.4

 

 .

 .

 .

Тем не менее, иногда требуется удалить разделители, используемые при форматировании входных документов — это можно сделать при помощи элемента .

 

Элементы <xsl:strip-space> и <xsl:preserve-space>

Элемент дает указание процессору XSLT убрать все чистые узлы-разделители (также называемые «потребляемыми», expendable, узлами-разделителями) из. выходного документа. Чистый узел-разделитель состоит только из символов-разделителей и не содержит текст какого-либо другого вида. У этого элемента только один атрибут:

• elements (обязательный). Задает элементы, из которых нужно убрать символы-разделители. Представляет, собой список разделённых символами-разделителями NameTest (именами или обобщёнными именами с символами подстановок).

Элемент не включает содержимого. Например, чтобы удалить все узлы-разделители из planets.xml, можно применить следующим образом:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

 

  

  

  

 

Вот результирующий документ, полученный после применения этой таблицы стилей к planets.xml. Обратите внимание на то, что убраны все символы-разделители, в том числе все символы новой строки:

Mercury.055358.651516.98343.4Venus.815116.753716.94366.8Earth1121071128.4

Заметьте, что таким образом удаляются только чистые узлы-разделители. Например, текст элемента Volcanoes for Dinner не содержит чистых текстовых узлов-разделителей, поэтому текст «Volcanoes for Dinner» будет сохранен в выходном документе вместе с пробелами. Так будет даже тогда, когда текст будет содержать несколько пробелов подряд, как в «Volcanoes for Dinner».

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

• elements (обязательный). Задает элементы, в которых нужно сохранить символы-разделители. Представляет собой список разделенных символами-разделителями NameTest (именами или обобщенными именами с символами подстановок).

Фактически элемент является элементом по умолчанию для всех элементов в XSLT. Если вы использовали элемент , все равно можно указать, в каком элементе или элементах нужно сохранить узлы-разделители, установив атрибут elements элемента в список этих элементов:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

 

 

  

  

  

 

Обсужденные средства удаления и сохранения разделителей могут показаться слишком сложными для форматирования выходных документов выравнивающими пробелами, но, к счастью, существует простой способ: атрибут indent элемента позволяет автоматически выровнять выходной документ.

 

Автоматическое выравнивание

Элемент поддерживает атрибут indent который устанавливается в «yes» или «no», и указывает процессору XSLT, нужно ли выравнивать результирующий документ. Как правило, выравнивание результирующего документа не имеет большого значения, поскольку с ним работает приложение, которому все равно, выровнен документ или нет, как мы видели в примерах преобразований XML- XML и XML-HTML. Однако иногда требуется представить результирующий документ в виде простого текста, и в таких случаях выравнивание документа для отображения иерархической структуры может оказаться удобным.

Способ работы процессора XSLT с переменной выравнивания не регламентируется W3C и зависит от процессора, поэтому для получения требуемого результата нужно экспериментировать. Пусть, например, у нас есть версия planets.xml без какого-либо выравнивания:

Mercury

.0553

58.65

1516

.983

43.4

Venus

.815

116.75

3716

.943

66.8

Earth

1

1

2107

1

128.4

При помощи элемента можно указать процессору XSLT осуществлять выравнивание документа при преобразовании его в HTML (листинг 3.6).

Листинг 3.6. Таблица стилей, задающая выравнивание

The Planets Table

The Planets Table

Name Mass Radius Day>/TD>

Результат применения таблицы с использованием процессора Saxon (в котором особенно хорошо реализовано выравнивание) с требуемым выравниванием:

 

 

  </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">  

 

 

  

   The Planets Table

  

 

   

   

  

   

   

    

   

    

    

   

   

    

   

   

   

  

  

   

    

   

    

   

 

Name Mass Radius Day
Mercury .0553 1516 58.65
Venus .815 3716 116.75
Earth 1 2107 1

 

Как видите, в XSLT обработке символов-разделителей приходится уделять достаточное внимание, но процедура упрощается, если вы знаете, что происходит.

ВЫРАВНИВАНИЕ ДОКУМЕНТОВ В ЭТОЙ КНИГЕ

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

 

Правила по умолчанию в шаблоне

Взгляните на следующую таблицу стилей XSLT — в ней заданы правила для выбора корневого узла, узлов и узлов :

 xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">

 

  

   

  

 

 

 

 

 

  

  

 

 

Обратите внимание на правило для элемента : в нем просто используется элемент для применения шаблонов ко всем дочерним узлам. Однако при обработке шаблона существует правило по умолчанию: если для элемента не задано правило, автоматически вызывается . Таким образом, следующая таблица стилей, в которой опущено правило для , делает в точности то же, что и предыдущая:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

  

 

 

  

  

  

 

В этом случае я воспользовался преимуществом правил по умолчанию для шаблона. Ниже приведены правила для каждого вида узлов, которые будут применены, если не задать правило для узла явно:

• Корневой узел. По умолчанию вызывается ;

• Узлы элементов. По умолчанию вызывается ;

• Узлы атрибутов. Копирует в результирующий документ значение атрибута, однако копирует его как текст, но не как атрибут;

• Текстовые узлы. Копирует в результирующий документ текст;

• Узлы комментариев. Нет обработки XSLT, ничего не копируется;

• Узлы инструкций обработки. Нет обработки XSLT, ничего не копируется;

• Узлы пространств имен. Нет обработки XSLT, ничего не копируется.

Наиболее важное правило по умолчанию применяется к элементам и может быть выражено следующим образом:

 

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

Правило по умолчанию для текстовых узлов можно выразить следующим образом: функция XSLT text выбирает текст узла, так что текст текстового узла добавляется в выходной документ:

 

Правило по умолчанию того же вида применяется к атрибутам, которые добавляются в выходной документ при помощи следующего правила, где выражение "@*" выбирает любой атрибут:

 

По умолчанию инструкции обработки не вставляются в выходной документ, поэтому их правило по умолчанию можно выразить просто при помощи следующей функции-инструкции обработки XSLT, которая выбирает инструкции обработки (как мы увидим в главе 8):

То же верно для комментариев — их правило по умолчанию может быть выражено при помощи функции XSLT comment, которая также будет рассмотрена в главе 8:

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

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

А вот результат применения этой таблицы стилей к planets.xml. Заметьте, что правило по умолчанию для атрибутов не применялось, потому что они не являются дочерними по отношению к другим узлам:

Mercury

.0553

58.65

1516

.983

43.4

Venus

.815

116.75

3716

.943

66.8

Earth

1

1

2107

1

128.4

ПРАВИЛА ПО УМОЛЧАНИЮ И INTERNET EXPLORER

Одна из проблем при работе с XSLT в Internet Explorer 5.5 или младше состоит в том, что браузер не предоставляет никаких правил по умолчанию. Необходимо задавать все правила самостоятельно, если только не установлен процессор MSXML3 в режиме замены (подробнее см. главу 2) или вы не обновили браузер до Internet Explorer 6.0.

Кроме того, узлы-разделители исходного документа сохраняются, поэтому можно считать, что следующее правило также является правилом по умолчанию: .

 

Удаление содержимого

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

Предположим, нам нужно удалить из planets.xml все данные о планетах, за исключением их названий и данных о массе. Следующая таблица стилей выполняет данную задачу.

Листинг 3.7. Удаление содержимого

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

 

  

 

 

 

 

 

  

  

  

 

 

  

  

  

 

 

  

  

   

  

 

 

 

 

 

 

 

 

 

А вот результирующий документ (отметьте, что я сохранил только элементы и ):

 Mercury

 .0553(Earth = 1)

 Venus

 .815(Earth = 1)

 Earth

 1(Earth = 1)

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

 

Разрешение конфликтов шаблонов

Еще одним важным аспектом работы с шаблонами является разрешение конфликтов. Если двум шаблонам удовлетворяют один и тот же узел или набор узлов, для определения применяемого шаблона XSLT учитывает их приоритет.

У каждого шаблона есть приоритет по умолчанию, основанный на значении атрибута select. Как правило, чем более сужающим является правило выбора или выражение (например, "PLANET" и "*"), тем выше его приоритет. В главе 4 мы рассмотрим, как процессор определяет приоритеты и как он работает с шаблонами, имеющими одинаковый приоритет.

Приоритет шаблона можно установить при помощи атрибута priority. В приведенном ниже листинге 3.8 правило, созданное элементом , имеет меньший приоритет, чем правило, созданное элементом .

Листинг 3.8. Установка приоритета шаблона

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

    </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

  

   

    

     The Planets Table

    

   

     

     

     

      

      

     

    

    

Name Mass Radius Day

   

  

 

 

  

  

   

   

   

  

 

 

  

  (Very heavy)

 

 

 

 

 

  

  

  

 

 

  

  

  

 

Процессор XSLT выбирает шаблон с наивысшим приоритетом, который добавляет текст "(Very heavy)" после каждого значения массы. В следующем примере использован шаблон с наивысшим приоритетом:

 

  </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

  

   

   

   

    

    

   

  

   

   

   

    

   

  

   

   

   

    

   

 

Name Mass Radius Day
Mercury .0553(Very heavy) 1516 miles 58.65 days
Venus .815(Very heavy) 3716 miles 116.75 days
Earth 1(Very heavy) 2107 miles 1 days

 

УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

Вопрос приоритета шаблонов должен быть учтен в XSLT 2.0. В частности, W3C рассматривает возможность добавления нового элемента с предварительным названием <xsl:next-match/>, который позволит выбирать для шаблона второй лучше всего подходящий элемент.

О приоритетах полезно знать еще одно: если двум шаблонам удовлетворяет один и тот же узел, и этим шаблонам не были присвоены приоритеты, процессор XSLT выберет шаблон с более узким правилом выбора. Например, условию "PLANET" будет отдано предпочтение перед обобщенным условием "*".

 

Элемент <xsl:copy>

Элемент позволяет скопировать узел из исходного дерева в выходное. Заметьте, однако, что это поверхностное (shallow) копирование, при котором не копируются потомки и атрибуты узла. У элемента есть один атрибут:

• use-attribute-sets. Задает названия наборов атрибутов, которые нужно применить к создаваемому элементу. Принимает значение списка QName, разделенных символами-разделителями. Этот атрибут можно использовать только в том случае, когда контекстный узел является элементом. Дополнительная информация о наборах атрибутов приведена в главе 6. 

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

Приведенная в листинге 3.9 таблица стилей впервые появилась в главе 2; все, что она делает, — копирует все элементы из исходного документа в результирующий.

Листинг 3.9. Таблица стилей, копирующая элементы

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

 

 

 

  

  

 

Однако не копирует атрибуты — вот результат применения этой таблицы стилей к planets.xml:

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 

  Venus

  .815

  116.75

  3716

  .943

  66.8

 

 

  Earth

  1

  1

  2107

  1

  128.4

 

Копирование атрибутов несколько сложнее, потому что нужно найти какой-либо способ применить к каждому атрибуту элемента. Это можно сделать, например, при помощи элемента , о котором пойдет речь в главе 5.

Листинг 3.10. Копирование атрибутов

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

  

   

   

  

 

 

А вот результат — заметьте, что на этот раз атрибуты не затронуты:

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 

  Venus

  .815

  116.75

  3716

  .943

  66.8

 

 

  Earth

  1

  1

  2107

  1

  128.4

 

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

ГЛУБОКОЕ КОПИРОВАНИЕ

Пример использования <xsl:copy> для осуществления глубокого копирования документа будет приведен в главе 4, в которой описывается функция узла и объясняется, как рекурсивно вызывать один и тот же шаблон.

 

Элемент <xsl:copy-of>

Элемент позволяет осуществлять глубокое копирование узлов, при котором копируется не только узел, но и все его атрибуты и потомки. У этого элемента единственный атрибут:

• select (обязательный). Узел или набор копируемых узлов. Этот элемент пуст и не имеет содержимого.

Ниже приведен пример работы этого элемента; в этом случае я заменил в листинге 3.10 элемент элементом , который явно выбирает для копирования все атрибуты контекстного элемента.

Листинг 3.11. Применение <copy-of>

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

  

   

  

 

Этот код работает так же, как и предыдущий пример, копируя все элементы и атрибуты. С другой стороны, можно вообще обойтись без каких-либо изменений в листинге 3.10, — я могу просто использовать для того, чтобы скопировать весь документ, выбрав корневой узел и скопировав всех его потомков:

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

 

 

При помощи можно также копировать конкретные узлы и их потомков вместо того, чтобы задавать подстановку *. Например, следующее правило копирует все элементы и их потомков:

 

По этой причине я могу заменить элемент на элемент :

 

 

Элемент <xsl:message>

При помощи элемента можно дать указание процессору XSLT отобразить сообщение и, по выбору, прекратить обработку таблицы стилей. У элемента один атрибут:

• terminate (необязательный). Значение «yes» прекращает обработку. По умолчанию установлено «no».

Куда на самом деле будет отправлено сообщение, зависит от процессора XSLT. Для процессоров, основанных на Java, сообщение обычно отправляется в выходной поток ошибок Java, которому соответствует экран компьютера, если процессор XSLT был вызван из командной строки. Другие процессоры XSLT могут выводить сообщения во всплывающие окна или в web-страницы, отправляемые в браузеры.

В приведенном ниже листинге 3.12 я прекращаю обработку XSLT, когда процессор XSLT пытается преобразовать элемент в planets.xml, выводя сообщение "Sorry, DAY information is classified." (Извините, информация о параметре «ДЕНЬ» засекречена.).

Листинг 3.12. Применение <xsl:message>

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

    

   

    

     

     

     

     

    

Name Mass Radius Day

   

  

 

 

 

  

   

  

  

  

 

 

 

  

  

 

 

  

  

  

 

 

 

   Sorry. DAY information is classified.

  

 

Вот результаты применения этой таблицы стилей в Xalan:

C:\planets>java org.apache.xalan xslt.Process -IN planets.xml -XSL message.xsl -OUT planets.html

file:///C:/XSL/messages/message.xsl: Line 49; Column 38;

Sorry. DAY information is classified.

XSLT Error (javax.xml.transform.TransformerException):

Stylesheet directed termination

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

Далее в книге будут рассмотрены и другие посвященные шаблонам темы — такие, как вызов именованных шаблонов и использование параметров. В следующей главе мы перейдем к большой и важной теме — созданию образцов выбора, при помощи которых в XSLT можно выбирать, какие узел или узлы вам нужны для работы. Мы уже затрагивали тему работы с образцами выбора, теперь же пора перейти к их систематическому рассмотрению.