Совершенствование таблиц стилей XSLT 2.0 посредством типов и схем

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

Такие функции XSLT 2.0, как type-aware и schema-aware, способны оказать существенное содействие при отладке таблицы стилей, а также улучшить качество таблицы стилей и повысить устойчивость при манипулировании данными. В предлагаемой статье описывается, как использовать функции XSLT 2.0 под названием type-aware (информация о типе) и schema-aware (информация о схеме) в процессе отладки и тестирования, чтобы избежать распространенных проблем с неверными маршрутами, с некорректными допущениями о типах данных и неверным указанием количества элементов. В статье приведены примеры XSLT-таблиц стилей с ошибками, которые не были бы выявлены без использования функций schema-aware. Кроме того, показано, как явное указание типов обуславливает получение полезных сообщений об ошибках.

Присцилла Уолмсли, управляющий директор, Datypic

Фото Присциллы УолмслиПрисцилла Уолмсли (Priscilla Walmsley) работает управляющим директором и старшим консультантом компании Datypic. Она специализируется на технологии, архитектуре и реализации XML. В последнее время Присцилла работает с Министерством юстиции США (через Trusted Federal Systems) над системой IEPD LEXS нa базе NIEM. Она – автор книг Definitive XML Schema (Prentice Hall, 2001 г.) и XQuery (O'Reilly Media, 2007 г.). Кроме того, она является соавтором учебника Web Service Contract Design and Versioning for SOA (Prentice Hall, 2008 г.). С Присциллой можно связаться по адресу: pwalmsley@datypic.com.



09.11.2012

XML-контент может быть сложным и непредсказуемым. При его обработке с использованием XSLT 1.0 может потребоваться большое количество "проб и ошибок" для формирования правильных выражений и для достижения способности к манипулированию всеми возможными структурами контента. Например, выражения XPath 1.0 с неправильно написанными именами не возвращают никакой информации (т. е., не предоставляют никаких полезных сообщений об ошибках), что существенно затрудняет отладку.

Часто используемые сокращения

  • HTML: Hypertext Markup Language (язык гипертекстовой разметки, язык HTML)
  • XHTML: Extensible HTML (расширяемый язык гипертекстовой разметки)
  • XML: Extensible Markup Language (Расширяемый язык разметки)
  • XSLT: Extensible Stylesheet Language Transformations (расширяемый язык преобразований таблиц стилей)

Версия XSLT 2.0 содержит значительные усовершенствования относительно версии 1.0. Кроме того, в нее были добавлены полезные и мощные функции type-aware (информация о типах) и schema-aware (информация о схеме). Декларирование типов для значений в XSLT-таблице стилей позволяет процессору сообщать вам о том, что вы сделали некорректные допущения о типе данных XML-контента или о количестве появлений определенного элемента/атрибута. Эти функциональные возможности делают сообщения об ошибках существенно полезнее. Импортирование XML-схем делает еще один шаг в нужном направлении, предоставляя процессору достаточную информацию о структуре входящего документа для того, чтобы он смог, наконец, сообщить вам о некорректных XPath-компонентах. Кроме того, эти функции предоставляют информацию о типах данных в контенте, что предотвращает выполнение операций, не имеющих смысла для соответствующего типа данных.

Использование типов в языке XSLT

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

  • В элементе xsl:variable или xsl:param для указания типа переменной или параметра, соответственно
  • В элементе xsl:template или xsl:function для указания возвращаемого типа шаблона или функции, соответственно
  • В элементе xsl:sequence для указания типа последовательности
  • В элементе xsl:with-param для указания типа значения, переданного в шаблон

Значение атрибута as представляет собой тип последовательности (sequence type).

Использование встроенных типов XML-схемы

Распространенный тип последовательности, который можно использовать в атрибуте as – название конкретного типа данных, напр., строка или целое число. В XSLT используется один из типов данных, встроенных в спецификацию XML-схемы. Для этого к имени типа добавляется префикс, например, xs: и декларируется пространство имен xs: в таблице стилей. В таблице 1 перечислены широко используемые типы данных XML-схемы.

Таблица 1. Таблица 1. Распространенные типы данных XML-схемы
Имя типа данныхОписаниеПримеры
stringЛюбая текстовая строкаabc
integerЦелое число любого размера1, 2
decimalДесятичное число1.2, 5.0
doubleЧисло удвоенной точности с плавающей точкой1.2, 5.0
dateДата в формате YYYY-MM-DD2009-12-25
timeВремя в формате HH:MM:SS12:05:04
booleanЗначение true ("истина") / false ("ложь")true, false
anyAtomicTypeЗначение любого из простых типовстрока, 123, false, 2009-12-25

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

<variable name="effDate" select="bill/effDate" as="xs:date"/>

Простые значения, такие как последовательности и даты, носят название атомарные значения. Обратите внимание, что effDate— это элемент; его контент будет преобразован в атомарное значение типа xs:date (при условии, что с входящим документом не ассоциирована никакая схема).

Типы последовательностей, представляющих XML-узлы

Для представления различных узлов в XML-дереве можно использовать более характерные типы последовательностей, перечисленные в таблице 2. С этими типами последовательности префикс xs: не используется, поскольку они не относятся к типам XML-схемы.

Таблица 2. Типы последовательностей, представляющих XML-узлы
Типы последовательностейОписание
element()Любой элемент
element(book)Любой элемент с именем book
attribute()Любой атрибут
attribute(isbn)Любой атрибут с именем isbn
text()Любой текстовый узел
node()Узел любого вида (элемент, атрибут, текстовый узел и т. д.)
item()Узел любого вида или атомарное значение любого вида (например, строка, целое число)

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

<variable name="effDate" select="//effDate" as="element()"/>

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

Использование индикаторов появления

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

Таблица 3. Использование индикаторов появления
Индикатор появленияОписание
*Нуль раз — много раз
?Нуль раз — один раз
+Один раз — много раз
(индикатор появления отсутствует)Один и только один раз

Например, если я хочу выразить, что с переменной (variable) связано значение zero to many (нуль раз — много раз), я могу сделать это следующим образом

<variable name="effDate" select="//effDate" as="element()*"/>

Нулевые элементы (любого вида) также носят название пустая последовательность.


Использование типов для повышения устойчивости таблицы стилей

Может возникнуть вопрос, каким образом использование атрибута as улучшает таблицы стилей. Рассмотрим несколько примеров. Вы можете загрузить все примеры кода для этой статье в виде zip-файла (см. раздел Загрузка).

Сценарий 1. Использование типов для принудительного задания количества элементов

В листинге 1 показан входящий документ, который содержит две книги.

Листинг 1. Пример входа (файл books.xml)
<books xmlns="http://www.datypic.com/books/ns">
  <book>
    <title>The East-West House: Noguchi's Childhood in Japan</title>
    <author>
      <person>
        <first>Christy</first>
        <last>Hale</last>
      </person>
    </author>
    <price>9.95</price>
    <discount>1.95</discount>
  </book>
  <book>
    <title>Buckminster Fuller and Isamu Noguchi: Best of Friends</title>
    <author>
      <person>
        <first>Shoji</first>
        <last>Sadao</last>
      </person>
    </author>
    <price>65.00</price>
    <discount>5.00</discount>
  </book>
</books>

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

Листинг 2. XSLT-сценарий 1, исходная версия (xslt_before.xsl)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:dtyf="http://www.datypic.com/functions"
  xpath-default-namespace="http://www.datypic.com/books/ns">

  <xsl:output method="html"/>

  <xsl:template match="books">
    <html>
      <body>
        <table border="1">
          <xsl:apply-templates/>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="book">
    <tr>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="dtyf:format-person-name(author/person)"/></td>
      <td><xsl:value-of select="price"/></td>
    </tr>
  </xsl:template>

  <xsl:function name="dtyf:format-person-name">
    <xsl:param name="person"/>
    <xsl:value-of select="$person/last"/>
    <xsl:text>, </xsl:text>
    <xsl:value-of select="$person/first"/>
  </xsl:function>
</xsl:stylesheet>

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

Рисунок 1. Результаты применения исходного XSLT-сценария 1 к файлу books.xml
Image showing the results of original Scenario 1 XSLT on books.xml

Предположим, что я пытаюсь выполнить этот сценарий с более разнообразными тестовыми данными, такими как в файле morebooks.xml (листинг 3).

Листинг 3. Более разнообразный входящий документ (morebooks.xml)
<books xmlns="http://www.datypic.com/books/ns">
  <book>
    <title>Isamu Noguchi: Sculptural Design</title>
    <author>
      <institution>Vitra Design Museum</institution>
    </author>
    <price>125.00</price>
  </book>
  <book>
    <title>The East-West House: Noguchi's Childhood in Japan</title>
    <author>
      <person>
        <first>Christy</first>
        <last>Hale</last>
      </person>
    </author>
    <price>9.95</price>
    <discount>1.95</discount>
  </book>
  <book>
    <title>Buckminster Fuller and Isamu Noguchi: Best of Friends</title>
    <author>
      <person>
        <first>Shoji</first>
        <last>Sadao</last>
      </person>
    </author>
    <price>65.00</price>
    <discount>5.00</discount>
  </book>
  <book>
    <title>Isamu Noguchi and Modern Japanese Ceramics: A Close Embrace
               of the Earth</title>
    <author>
      <person>
        <first>Louise</first>
        <middle>Allison</middle>
        <last>Cort</last>
      </person>
    </author>
    <author>
      <person>
        <first>Bert</first>
        <last>Winther-Tamaki</last>
      </person>
    </author>
    <author>
      <person>
        <first>Bruce</first>
        <middle>J.</middle>
        <last>Altshuler</last>
      </person>
    </author>
    <author>
      <person>
        <first>Ryu</first>
        <last>Niimi</last>
      </person>
    </author>
    <price>29.95</price>
    <discount>5.95</discount>
  </book>
</books>

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

Рисунок 2. Результаты применения исходного XSLT-сценария 1 к файлу morebooks.xml
Image of the results of original Scenario 1 XSLT on morebooks.xml

Для повышения устойчивости таблицы стилей можно исправить функцию, как показано в листинге 4, где я добавила атрибуты as и к параметру, и к функции (возвращаемый тип). Это в явном виде реализует допущения, которые я делала, когда я писала эту функцию.

Листинг 4. XSLT-сценарий 1, доработанная функция
<xsl:function name="dtyf:format-person-name" as="xs:string*">
  <xsl:param name="person" as="element()"/>
  <xsl:value-of select="$person/last"/>
  <xsl:text>, </xsl:text>
  <xsl:value-of select="$person/first"/>
</xsl:function>

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

  • An empty sequence is not allowed as the first argument of dtyf:format-person-name() (Пустая последовательность не допускается в качестве первого аргумента для dtyf:format-person-name() )— в случае автора-организации
  • A sequence of more than one item is not allowed as the first argument of dtyf:format-person-name() (Последовательность из более чем одного элемента не допускается в качестве первого аргумента для dtyf:format-person-name() )— в случае нескольких авторов

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

Листинг 5. XSLT-сценарий 1, итоговая версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:dtyf="http://www.datypic.com/functions"
  xpath-default-namespace="http://www.datypic.com/books/ns">

  <xsl:output method="html"/>

  <xsl:template match="books">
    <html>
      <body>
        <table border="1">
          <xsl:apply-templates/>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="book">
    <tr>
      <td><xsl:value-of select="title"/></td>
      <td>
        <xsl:for-each select="author">
          <xsl:choose>
            <xsl:when test="person">
              <xsl:value-of select="dtyf:format-person-name(person)"/>
            </xsl:when>
            <xsl:when test="institution">
              <xsl:value-of select="institution"/>
            </xsl:when>
          </xsl:choose>
          <xsl:if test="position() != last()">
            <br/>
          </xsl:if>
        </xsl:for-each>
      </td>
      <td><xsl:value-of select="price"/></td>
    </tr>
  </xsl:template>

  <xsl:function name="dtyf:format-person-name" as="xs:string*">
    <xsl:param name="person" as="element()"/>
    <xsl:value-of select="$person/last"/>
    <xsl:text>, </xsl:text>
    <xsl:value-of select="$person/first"/>
  </xsl:function>
</xsl:stylesheet>

Мои результаты на рисунке 3 выглядят значительно лучше — теперь они корректно демонстрируют автора-организацию и нескольких авторов.

Рисунок 3. Результаты применения итогового XSLT-сценария 1 к файлу morebooks.xml
Image of the results of final Scenario 1 XSLT on morebooks.xml

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

Сценарий 2. Использование типов, чтобы гарантировать корректную интерпретацию операторов

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

Листинг 6. XSLT-сценарий 2, исходная версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:dtyf="http://www.datypic.com/functions"
 xpath-default-namespace="http://www.datypic.com/books/ns">

 <xsl:output method="html"/>

 <xsl:template match="books">
  <html>
   <body>
    <table border="1">
     <xsl:apply-templates/>
    </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="book">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="author"/></td>
   <td><xsl:value-of select="dtyf:calculate-price(price, discount)"/></td>
  </tr>
 </xsl:template>

 <xsl:function name="dtyf:calculate-price">
  <xsl:param name="price"/>
  <xsl:param name="discount"/>
  <xsl:sequence select="$price - $discount"/>
 </xsl:function>

</xsl:stylesheet>

Как и в предыдущем примере, все работает прекрасно с файлом books.xml (за исключением ошибки округления), но при работе с файлом morebooks.xml сценарий не обрабатывает книги, на которые не установлено скидок (см. рисунок 4). Поскольку значение $discount является пустой последовательностью, любая арифметическая операция с пустой последовательности возвращает также пустую последовательность.

Рисунок 4. Результаты применения исходного XSLT-сценария 2 к файлу morebooks.xml
Image of the results of original Scenario 2 XSLT on morebooks.xml

Для исправления этой ситуации сначала я собиралась ограничиться сравнением элемента $discount с нулем, однако затем я подумала, что функция будет еще более устойчивой, если я удостоверюсь в том, что величина скидки (discount) меньше чем величина цены (price). Мне совсем не нужны отрицательные цены в моей таблице. По указанной причине я также включила сравнение $discount < $price (см. Листинг 7).

Листинг 7. XSLT-сценарий 2, исправленная версия
 <xsl:function name="dtyf:calculate-price">
  <xsl:param name="price"/>
  <xsl:param name="discount"/>
  <xsl:choose>
   <xsl:when test="$discount > 0 and $discount &lt; $price">
    <xsl:sequence select="$price - $discount"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:sequence select="$price"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:function>

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

Рисунок 5. Результаты применения исправленного XSLT-сценария 2 к файлу morebooks.xml
Image of results of revised Scenario 2 XSLT on morebooks.xml

Эта проблема возникает вследствие того, что XML-код сравнивает цену и скидку как строки. Таково поведение по умолчанию для операторов сравнения, если для их операндов не указаны типы. Строка 5.95 больше, чем строка 29.95, поэтому сравнение возвращает результат false (ложь), а не true (истина). Добавление типов к моей схеме устраняет эту эту проблему (см. листинг 8).

Листинг 8. XSLT-сценарий 2, итоговая версия
<xsl:function name="dtyf:calculate-price" as="xs:decimal">
  <xsl:param name="price" as="xs:decimal"/>
  <xsl:param name="discount" as="xs:decimal?"/>
  <xsl:choose>
    <xsl:when test="$discount > 0 and $discount &lt; $price">
       <xsl:sequence select="$price - $discount"/>
    </xsl:when>
    <xsl:otherwise>
       <xsl:sequence select="$price"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

Я указала, что оба параметра являются десятичными числами, и применяю индикатор появления ?, чтобы разрешить использование пустой последовательности для скидки, поскольку скидка может отсутствовать. В исправленном XSLT-коде значения элементов price (цена) и discount (скидка) преобразуются в десятичные числа при вызове функции (это происходит автоматически, когда для входных данных не указан тип, т.е. не имеется схемы). Теперь XML-код сравнивает цену и скидку как числа, поэтому я получаю правильные результаты (рис. 6).

Рисунок 6. Результаты применения итогового XSLT-сценария 2 к файлу morebooks.xml
Image of results of final Scenario 2 XSLT on morebooks.xml

Моя ошибка округления также пропала, поскольку по умолчанию оператор вычитания интерпретирует числа как значения вида xs:double, т. е., как числа с плавающей запятой, которые не обрабатываются с такой большой точностью, как десятичные значения xs:decimal.


Использование схем для улучшения таблиц стилей

Предыдущие примеры используют встроенные типы XML-схемы , но не требуют наличия схемы у входящего документа. Теперь посмотрим, как использование XML-схемы позволяет дополнительно повысить устойчивость XSLT-кода. Schema-aware – это опциональная функция XSLT-процессоров; таблицы стилей в этом параграфе должны исполняться с помощью XSLT-процессора, поддерживающего эту функцию. Примеры для этой статьи были протестированы с помощью XSLT-процессора Saxon-EE (соответствующая ссылка на дополнительную информацию приведена в разделе Ресурсы).

Импортирование и использование схем

Предположим, у вас есть схема для документа books.xml, которая носит имя books.xsd (см. листинг 9). Она определяет все типы данных и количества всех элементов. Полное объяснение XML-схемы выходит за рамки данной статьи: В качестве вводного учебного пособия я рекомендую использовать документ XML Primer (см. раздел Ресурсы).

Листинг 9. XML-схема Books
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://www.datypic.com/books/ns"
  xmlns="http://www.datypic.com/books/ns" elementFormDefault="qualified">
  <xs:element name="books" type="BooksType"/>

  <xs:complexType name="BooksType">
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:element name="book" type="BookType"/>
  <xs:complexType name="BookType">
    <xs:sequence>
      <xs:element ref="title"/>
      <xs:element ref="author" maxOccurs="unbounded"/>
      <xs:element ref="price"/>
      <xs:element ref="discount"  minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:element name="title" type="xs:string"/>
  <xs:element name="author" type="AuthorType"/>
  <xs:element name="price" type="xs:decimal"/>
  <xs:element name="discount" type="xs:decimal"/>
  <xs:complexType name="AuthorType">
    <xs:choice>
      <xs:element ref="person"/>
      <xs:element ref="institution"/>
    </xs:choice>
  </xs:complexType>

  <xs:element name="person" type="PersonType"/>
  <xs:element name="institution" type="xs:string"/>
  <xs:complexType name="PersonType">
    <xs:sequence>
      <xs:element ref="first"/>
      <xs:element ref="middle" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="last"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="first" type="xs:string"/>
  <xs:element name="middle" type="xs:string"/>
  <xs:element name="last" type="xs:string"/>

</xs:schema>

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

Таблица 4. Типы последовательностей с поддержкой информации о схеме (Schema-aware)
Пример типа последовательностиСмысл
element(*,BookType)Любой элемент типа BookType
element(book,BookType)Любой элемент типа BookType с именем book
schema-element(book)Любой элемент, прошедший валидацию на соответствие глобальной декларации для book в схеме, или являющийся членом ее группы substitution group
attribute(*,ISBNType)Любой атрибут типа ISBNType
attribute(isbn,ISBNType)Любой атрибут типа ISBNType с именем isbn
schema-attribute(isbn)Любой атрибут, прошедший валидацию на соответствие глобальной декларации для isbn в схеме, или являющийся членом ее группы substitution group

Сценарий 3. Обнаружение некорректных маршрутов

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

Листинг 10. XSLT-сценарий 3, исходная версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xpath-default-namespace="http://www.datypic.com/books/ns">

  <xsl:output method="html"/>
  <xsl:template match="books">
    <html>
      <body>
        <table border="1">
          <xsl:apply-templates/>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="book">
    <tr>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="author/last"/></td>
      <td><xsl:value-of select="author/first"/></td>
      <td><xsl:value-of select="prce"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

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

Рисунок 7. Результаты применения исходного XSLT-сценария 3 к файлу books.xml
Image of results of original Scenario 3 XSLT on books.xml

Мне поможет задействование функции schema-aware в моей таблице стилей. Во-первых, нужно импортировать схему (см. листинг 11), а затем присвоить ей имя файла схемы (абсолютное или относительное) и целевое пространство имен этой схемы. Кроме того, для реализации необходимого контроля за ошибками я должна изменить типы последовательностей, используемых в моем атрибуте match, чтобы задействовать элемент schema-element().

Листинг 11. XSLT-сценарий 3, исправленная версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xpath-default-namespace="http://www.datypic.com/books/ns">

  <xsl:output method="html"/>
  <xsl:import-schema namespace="http://www.datypic.com/books/ns"
                     schema-location="books.xsd"/>
  <xsl:template match="schema-element(books)">
    <html>
      <body>
        <table border="1">
          <xsl:apply-templates/>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="schema-element(book)">
    <tr>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="author/last"/></td>
      <td><xsl:value-of select="author/first"/></td>
      <td><xsl:value-of select="prce"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Теперь я получаю следующие три сообщения об ошибках.

  • The complex type AuthorType does not allow a child element named last (Комплексный тип AuthorType не позволяет именовать последним дочерний элемент)
  • The complex type AuthorType does not allow a child element named first (Комплексный тип AuthorType не позволяет именовать первым дочерний элемент)
  • The complex type BookType does not allow a child element named prce (Комплексный тип BookType не позволяет давать дочернему элементу имя prce)

Первые два сообщения об ошибках говорят о том, что я указала неверный маршрут; я забыла о том, что элементе person является потомком элемента author. Третье сообщение об ошибке помогает понять, что я допустила ошибку в слове price. В листинге 12 реализованы исправления для маршрутов.

Листинг 12. XSLT-сценарий 3, итоговая версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xpath-default-namespace="http://www.datypic.com/books/ns">

 <xsl:output method="html"/>
 <xsl:import-schema namespace="http://www.datypic.com/books/ns"
                    schema-location="books.xsd"/>
 <xsl:template match="books">
  <html>
   <body>
    <table border="1">
     <xsl:apply-templates/>
    </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="schema-element(book)">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="author/person/last"/></td>
   <td><xsl:value-of select="author/person/first"/></td>
   <td><xsl:value-of select="price"/></td>
  </tr>
 </xsl:template>

</xsl:stylesheet>

Сценарий 4. Валидация выхода

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

Для подтверждения валидности выходного результата сначала нужно импортировать схему, точно так же, как я делала это для схемы входящего документа. Кроме того, я сообщаю процессору, что хочу осуществить валидацию выходного результата. С этой целью я добавляя атрибут xsl:validation="strict" к корневому элементу. После этого весь контент html будет подвергнут валидации.

Я вношу еще одно изменение, делая XHTML-пространство имен пространством имен по умолчанию, поскольку валидный вывод должен находиться в корректном пространстве имен. Соответствующие изменения показаны в листинге 13.

Листинг 13. XSLT-сценарий 4, полученный посредством исправления Сценария 3
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns="http://www.w3.org/1999/xhtml"
 xpath-default-namespace="http://www.datypic.com/books/ns">

 <xsl:output method="xhtml"/>
 <xsl:import-schema namespace="http://www.datypic.com/books/ns"
                    schema-location="books.xsd"/>
 <xsl:import-schema namespace="http://www.w3.org/1999/xhtml"
                    schema-location="xhtml1-strict.xsd"/>

 <xsl:template match="books">
  <html xsl:validation="strict">
   <body>
    <table border="1">
     <xsl:apply-templates/>
    </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="schema-element(book)">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="author/person/last"/></td>
   <td><xsl:value-of select="author/person/first"/></td>
   <td><xsl:value-of select="price"/></td>
  </tr>
 </xsl:template>

</xsl:stylesheet>

При исполнении этого кода я получаю следующее сообщение об ошибке.

  • In content of element <html>: The content model does not allow element <body> to appear here. Expected: {http://www.w3.org/1999/xhtml}head

Это сообщение говорит мне о том, что я упустила элемент head. После нескольких итераций я получаю итоговый XSLT-код (листинг 14), который генерирует строго валидный XHTML-текст.

Листинг 14. XSLT-сценарий 4, итоговая версия
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns="http://www.w3.org/1999/xhtml"
 xpath-default-namespace="http://www.datypic.com/books/ns">

 <xsl:output method="xhtml"/>
 <xsl:import-schema namespace="http://www.datypic.com/books/ns"
                    schema-location="books.xsd"/>
 <xsl:import-schema namespace="http://www.w3.org/1999/xhtml"
                    schema-location="xhtml1-strict.xsd"/>

 <xsl:template match="books">
  <html xsl:validation="strict">
   <head><title>Books</title></head>
   <body>
    <table border="1">
     <xsl:apply-templates/>
    </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="schema-element(book)">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="author/person/last"/></td>
   <td><xsl:value-of select="author/person/first"/></td>
   <td><xsl:value-of select="price"/></td>
  </tr>
 </xsl:template>

</xsl:stylesheet>

Заключение

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

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

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


Загрузка

ОписаниеИмяРазмер
Примеры XSLT-таблиц стилей для данной статьиexamples.zip10 КБ

Ресурсы

Научиться

Получить продукты и технологии

  • Saxon: Доступный для загрузки процессор XSLT 2.0 с поддержкой функции schema-aware, который использовался для тестирования примеров в данной статье.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML
ArticleID=845123
ArticleTitle=Совершенствование таблиц стилей XSLT 2.0 посредством типов и схем
publish-date=11092012