Практика XSLT
Зміст
Приклад трансформації XML-документа в HTML
XSLT — це потужний засіб для трансформації даних XML. Його часто використовують як web-шаблонізатор: на вхід подається XML-документ та XSLT-перетворення, а на виході отримують HTML-документ. Процес отримання HTML-документа з XML називають відображенням чи рендерингом.
Розглянемо типовий приклад рендерингу HTML.
Дано список музичних композицій як XML-документа.
Відобразимо цей документ у вигляді HTML ul/li списку, як показано нижче:
Список треків у HTML форматі
Для цього використовуємо наступне XSLT-перетворення:
Дане перетворення поверне нам наступний HTML:
XSLT-перетворення складається з трьох шаблонів (xsl: template). Кожен шаблон обслуговує свою сутність, що дає можливість легко вносити зміни і робить код зрозумілим.
Якщо нам потрібно змінити відображення списку (наприклад, додати атрибут class ), ми редагуємо шаблон match="PlayList" .
Якщо ми хочемо змінити відображення елементів списку, то тут, цілком очевидно, що варто змінювати шаблон match="Track" .
Фактично, XSLT не тільки дає нам можливість розділити дані та уявлення (це завдання будь-якого шаблонізатора), а й дозволяє розділяти уявлення різних сутностей.
Звісно, у складніших випадках поділу досягти такого поділу буває складно. Дуже легко дійти ситуації, коли виникає «божественний шаблон», який робить все, так само варто боятися скотитися до «стрільби дробом» купою дрібних шаблонів.
Налагодження XSLT
Що мені дуже подобається в XSLT, то це можливість налагодження. Налагодження допомагає наочно побачити логіку роботи XSTL, структуру документа, значення змінних.
Наприклад,налагодження допоможе побачити, що за сутність обробляє шаблон match = "/".
У Visual Studio налагодження XSLT запускається поєднанням клавіш ALT+F5 .

Додавши у вікно Watch XPath вираз "." (Точка), ми побачимо, що поточний елемент шаблону - це корінь (Root) документа. Тут можна розмістити контейнер div , або щось, що стосується всього XML-документа.
Робота з сутностями XML
Можна помітити, що в наведених прикладах є сутність — Ми можемо її використовувати, тому що визначили її на початку XSLT-документа
Таким чином, виводиться як символ з кодом .
Якщо потрібно вивести рядок «як є», варто використовувати CDATA наступним чином:
Елемент xsl:text
Хочу загострити увагу елементі xsl:text . Він дозволяє контролювати, що саме міститиме TEXT-елемент. Значимість xsl:text очевидна практично:
Як видно з прикладу вище, відсутність елемента xsl:text призвела до появи в HTML зайвих перекладів рядків та пропусків.
Безумовно, можна писати XSLT і без xsl:text наступним чином:
Такий шаблон важко читати і є велика ймовірність, що при супроводі в ньому з'являться помилки.
Потрібно намагатися, щоб форматування XSLT-шаблону не впливало на результат трансформації. Саме тому я вважаю, що використовувати xsl:text це хороша практика.
Для розгалужень XSLT є спеціальні елементи: xsl:if і xsl:choose . Але я вважаю, що цими інструментами дуже зловживають. Цікавіший прийом, що дозволяє не захаращувати шаблон розгалуженнями.
Розглянемо приклад реалізації розгалужень:
Доповнимо попередній приклад можливістю виводити повідомлення «Список порожній» у випадку, якщо PlayList не містить елементів Track .
Рішення з використанням xsl:choose буде таким:
Рішення з використанням додаткового шаблону буде наступним:
Друге рішення, на мій погляд, виглядає красивішим: нова функціональність не додала нового коду в старі шаблони, новий шаблон максимально ізольований.
Якщо потрібно додати картинку до повідомлення про порожній список, то в першому випадку швидше за все набухне елемент xsl:when у шаблоні match="PlayList" . А ось у другому випадку зміни будуть лише у спеціалізованому шаблоні.
У попередньому прикладі ми розділили дві абсолютно різні гілки рендерингу списку. Але що якщо гілки різняться трохи? Тут використання xsl:if та xsl:choose цілком виправдане. Але мені хотілося б показати інший підхід: використання параметра mode у елемента xsl:template .
У наступному прикладі навісимо різні стилі на парні та непарні елементи списку.
Цикли та сортування у XSLT
Для циклів XSLT є елемент xsl:for-each , але подібний ефект можна отримати, використовуючи звичайний xsl:apply-templates .
Виведемо список композицій, відсортований за тривалістю.
Варіант із використанням xsl:for-each :
Варіант із використанням xsl:apply-templates :
Як видно з коду, перший варіант коротший і простіший, але він порушив принцип поділу відповідальності для шаблонів. Тепер шаблон match="PlayList" став містити логіку відображення елемента Track.
Здавалося б, нічого страшного, але представимо завдання, коли у списку зустрічаються композиції з Id і без. Для композиції з Id потрібно відрендерити посилання, а інших вивести лише текст.
Варіант із використанням xsl:for-each :
Варіант із використанням xsl:apply-templates :
У випадку xsl:for-each нам знадобилосядодавати розгалуження, а у випадку xsl:apply-templates - новий шаблон.
Якби шаблон match="PlayList" вже містив розгалуження та логіку, то нам знадобився деякий час, щоб розібратися, куди саме нам потрібно вставити гілку. Варіант із xsl:apply-templates позбавлений цього недоліку, оскільки ми лише декларуємо новий шаблон, а не намагаємося впровадитись у старі.
Використання xsl:for-each має ще одну небезпеку. Якщо ви бачите довільну ділянку коду всередині шаблону match="PlayList", то припускаєте, що поточний елемент це PlayList, однак xsl:for-each змінює контекст. Побачивши наступний код код:
Вам потрібно уважно придивитися до контексту, щоб зрозуміти, що select="." насправді вибирає поточний Track.
Шаблон mode="TrackName" match="Track" був доданий для запобігання дублювання коду, що відображає назву. Я не зробив цього раніше, бо цього не було потреби. Як тільки я помітив дублювання, я провів рефакторинг і виніс загальну логіку відображення нового шаблону.
xsl:for-each – це спосіб не плодити сутності. Ви просто додаєте логіку відображення всередину xsl:for-each і все чудово працює. Проблеми починаються потім, коли тіло циклу розростається, а проводити рефакторинг xsl:for-each набагато складніше, ніж виносити дубльований код.
Висновок
XSLT - це досить гнучкий інструмент, він дає можливість вирішити ваше завдання різними шляхами. Однак, при написанні XSLT варто приділяти особливу увагу шаблонам.
Сподіваюся, мої практичні поради допоможуть вам писати більш зрозумілий код.