![]() |
Александр Самиляк
26 июля 2011 |
|
![]() |
Задача. | Наглядно показать, чем хороши сущности в XSLT. |
![]() |
![]() |
![]() |
В студийных XSL-ях мы нередко используем сущности (они же entity или «энтити»). Куда же без них, ведь мы стараемся не забывать про типографику — то неразрывный пробел надо поставить, то длинное тире —. Но не только типографикой ограничивается польза сущностей. Об этой пользе и пойдет речь в данной статье.
Перво-наперво разберемся в том, как подключать сущности к
Начнем с более привычного подключения — внешнего. Оно имеет вид:
<!DOCTYPE имя корневого XML-элемента SYSTEM "путь до файла">
Такая запись часто присутствует в начале XSL-файлов:
<!DOCTYPE xsl:stylesheet SYSTEM "entities.dtd">
Здесь xsl:stylesheet — это имя корневого элемента любого XSL-документа, а entities.dtd — путь до файла, содержащего определения всех используемых сущностей. Кстати, путь до файла может быть не только локальным адресом, но и адресом в интернете:
<!DOCTYPE xsl:stylesheet SYSTEM "http://www.artlebedev.ru/tools/technogrette/xslt/entity-1/entities.dtd" >
Тогда трансформатор попытается скачать этот файл, а если ему это не удастся, выдаст ошибку парсинга шаблона. Такое поведение отлично от того, что происходит при определении неймспейса, где URI — это лишь уникальный идентификатор, не требующий скачивания никаких файлов.
Надо сказать, что все эти правила подключения касаются не столько сущностей, сколько языка DTD (Document Type Definition), особого языка, с помощью которого можно описывать структуру
Хозяйке на заметку |
DTD с внешним подключением делятся на два подвида: приватные, private, о которых знает узкий круг лиц (упомянутый entities.dtd), и публичные, public, о которых знает весь земной шар (HTML-доктайп). Указанный синтаксис относится только к приватным DTD. |
Подключив файл с сущностями, мы можем ими пользоваться. Например, в целях типографики:
<!DOCTYPE xsl:stylesheet SYSTEM "entities.dtd"> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> Трансформеры без Меган Фокс — незачет. </xsl:template> </xsl:stylesheet>
Польза внешнего подключения сущностей давно известна — это позволяет хранить в одном файле entities.dtd все глобальные сущности, которые могут понадобиться в разных XSL-файлах.
Обычно в этом entities.dtd объявляется множество разных сущностей, которые используются по всему проекту. Определение, скажем, неразрывного пробела выглядит так:
<!ENTITY nbsp " ">
Второй способ подключения сущностей — подключение внутреннее. Делается это так:
<!DOCTYPE имя корневого XML-элемента [ определение сущностей ] >
Перепишем последний пример, использовав внутреннее подключение:
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> <!ENTITY mdash "—"> ] > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> Трансформеры без Меган Фокс — незачет. </xsl:template> </xsl:stylesheet>
Этот способ удобен в случаях, когда у нас всего один XSL-файл и не хочется создавать отдельный файлик с сущностями. Например, когда мы пишем XSL-библиотеку, использующую какие-то сущности, и не хотим таскать за собой два файла. Тогда с помощью такого внутреннего подключения можно объявить сущности прямо в коде этого единственного XSL-файла.
И наконец, последний, самый интересный, способ — комбинированный. Он заключается в том, что мы можем одновременно как подключить сущности внешним файлом, так и определить новые прямо в XSL-шаблоне. Формат комбинированного подключения таков:
<!DOCTYPE имя корневого XML-элемента SYSTEM "путь до файла" [ определение сущностей ] >
Таким подключением удобно пользоваться, когда нам нужны сущности из глобального entities.dtd и при этом хочется определить какие-то константы, необходимые только в текущем XSL-файле (например, CSS-классы, используемые в нескольких местах).
Рассмотрим такую ситуацию на примере корзины в магазине. Это должна быть HTML-таблица, имеющая строку заголовков и несколько строк — товаров, положенных в корзину:
<!DOCTYPE xsl:stylesheet SYSTEM "entities.dtd" [ <!ENTITY CLASS_TITLE "title"> <!ENTITY CLASS_COST "cost"> ] > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <table class="cart"> <xsl:call-template name="cart_head" /> <xsl:call-template name="cart_content" /> </table> </xsl:template> <xsl:template name="cart_head"> <tr> <th class="&CLASS_TITLE;">Название</th> <th class="&CLASS_COST;">Стоимость</th> </tr> </xsl:template> <xsl:template name="cart_content"> <xsl:for-each select="&content;/cart/product"> <tr> <td class="&CLASS_TITLE;"> <xsl:apply-templates select="." mode="cart_content_product_title" /> </td> <td class="&CLASS_COST;"> <xsl:apply-templates select="." mode="cart_content_product_cost" /> </td> </tr> </xsl:for-each> </xsl:template> ... </xsl:stylesheet>
В этой таблице есть два столбца — название товара и его стоимость. CSS-классы для этих столбцов фигурируют у нас в двух местах, в заголовке таблицы и в теле, поэтому я вынес эти классы в отдельные внутренние сущности &CLASS_TITLE; и &CLASS_COST;. Но одновременно с этим я воспользовался сущностью &content;, определенной в entities.dtd. В этой сущности хранится XPath до элемента <content> входящего
А теперь самое «вкусное» место — сущности с внутренним подключением не видны нигде, кроме текущего XSL-файла. То есть если мы написали шаблон, определив внутри него нужные нам сущности, и наш шаблон кто-то импортировал, то наши сущности не будут мешаться ему под ногами. Более того, импортирующий шаблон внутри себя может определить сущность с таким же именем, как и у нас. При этом никакого конфликта имен не возникнет, каждый будет пользоваться своей сущностью, определенной в его XSL-файле.
Главный шаблон main.xsl:
<!DOCTYPE xsl:stylesheet [ <!ENTITY suchnost "main"> ] > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:import href="import.xsl" /> <xsl:template match="/"> Suchnost in main.xsl: &suchnost; <xsl:call-template name="import" /> </xsl:template> </xsl:stylesheet>
Импортируемый шаблон import.xsl:
<!DOCTYPE xsl:stylesheet [ <!ENTITY suchnost "import"> ] > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:template name="import"> Suchnost in import.xsl: &suchnost; </xsl:template> </xsl:stylesheet>
Выполнение главного шаблона выдаст результат:
Suchnost in main.xsl: main Suchnost in import.xsl: import
Такое поведение свойственно не только внутреннему подключению сущностей, но и внешнему тоже. Поэтому общее правило: сущности видны только в том XSL-файле, к которому они подключены, и ни в каком другом. Оно и понятно, ведь «сущность» — это термин, относящийся не только к XSL, а в целом к XML, который ничего не знает ни про какие импортирования шаблонов. Просто есть отдельно взятый
С внутренним подключением сущностей вышеозначенное правило нам сильно помогает: никто не увидит сущности, нужные только нам. Но есть и оборотная сторона медали — с внешним подключением файла entities.dtd это правило, наоборот, создает нам неудобства, так как этот entities.dtd необходимо подключать в каждом XSL-файле, желающем использовать глобальные сущности. Нельзя подключить entities.dtd к своему шаблону, просто проимпортировав другой шаблон, в котором есть это подключение.
В следующей части нас ждет небольшое сравнение сущностей с XSL-переменными.
© 19952023 Студия Артемия Лебедева
|