Для того, чтобы работать с данным документом, вам необходимо располагать как минимум браузером Internet Explorer версии 5.0 и выше. При этом будут работать некоторые из приведенных в тексте примеров.
Для того, чтобы у вас работали все примеры, необходимо установить XML-парсер версии 3. Если пример работает только под управлением XML-парсера версии 3, то в каждом случае это оговаривается особо. Отметим, что версии IE вплоть до 5.5 используют более ранние версии парсера, поэтому устанавливать его все равно придется. О более старших версиях IE узнайте самостоятельно.
Дистрибутив XML-парсера версии 3 можно найти по адресу http://download.microsoft.com/download/xml/Install/3.0/WIN98Me/EN-US/msxml3.exe.
После установки парсера вам нужно будет зарегистрировать его в реестре. Для этого в командной строке необходимо выполнить команду: regsvr32 msxml3.dll. Затем необходимо сообщить IE, что вы намерены использовать этот парсер. Для этого нужно запустить утилиту xmlinst. Утилиту xmlinst можно найти по адресу http://msdn.microsoft.com/msdn-files/027/001/469/xmlinst.exe. Вы можете также попробовать найти ответы на вопросы об установке XML-парсера по адресу http://www.netcrucible.com/xslt/msxml-faq.htm.
В предыдущих примерах порядок строк в таблице полностью соответствовал группам тегов в XML-файле. Этот порядок можно изменять. Добавим в тег
<xsl:for-each select="tutorial/enimals/dogs/dog">
атрибут order-by
<xsl:for-each select="tutorial/enimals/dogs/dog" order-by="dogName">
Наша таблица примет вид (ex03-3.xml, ex03-3.xsl).
Кличка |
Вес |
Цвет |
Бобик |
2 кг |
бело-серый |
Трезор |
25 кг |
черный |
Тузик |
10 кг |
белый с черными пятнами |
Шарик |
18 кг |
рыжий с черными подпалинами |
Более интересные результаты мы получим, если попытаемся отсортировать таблицу по столбцу "Вес". Вначале попробуем сделать по аналогии с предыдущим примером - атрибут order-by="dogName" заменим на order-by="dogWeight". Результат приведен ниже (ex03-4.xml, ex03-4.xsl).
Кличка |
Вес |
Цвет |
Тузик |
10 кг |
белый с черными пятнами |
Шарик |
18 кг |
рыжий с черными подпалинами |
Бобик |
2 кг |
бело-серый |
Трезор |
25 кг |
черный |
Таблица действительно отсортирована по столбцу "вес", но это не числовая, а строковая сортировка! Для того, чтобы браузер воспринял значения как числа, ему необходимо об этом сказать, - вместо order-by="dogWeight" необходимо написать order-by="number(dogWeight)". Теперь мы получили правильный результат (ex03-5.xml, ex03-5.xsl).
Кличка |
Вес |
Цвет |
Бобик |
2 кг |
бело-серый |
Тузик |
10 кг |
белый с черными пятнами |
Шарик |
18 кг |
рыжий с черными подпалинами |
Трезор |
25 кг |
черный |
Приведем теперь пример сортировки по нескольким столбцам. Различные элементы в атрибуте order-by должны разделяться символом ";" - order-by="number(dogWeight); dogName" (ex03-6.xsl). Таблица приведена ниже.
Кличка |
Вес |
Цвет |
Трезор |
10 кг |
черный |
Тузик |
10 кг |
белый с черными пятнами |
Бобик |
18 кг |
бело-серый |
Шарик |
18 кг |
рыжий с черными подпалинами |
Следующий пример работает только под управлением XML-парсера версии 3. В нем строки сортируются по одному столбцу - по кличке собаки. Этот пример уже приводился выше, однако теперь мы используем новый синтаксис (ex03-7.xml, ex03-7.xsl).
Отметим разницу.
При использовании нового синтаксиса используется ссылка на другое пространство имен
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Это очень важный момент, и его никогда нельзя упускать из виду.
Кроме того, мы убрали атрибут order-by в элементе xsl:for-each и добавили другой элемент
<xsl:sort order="ascending" select="dogName"/>
Если элемент xsl:sort присутствует в элементе xsl:for-each, то он всегда должен стоять сразу после элемента xsl:for-each. Синтаксис элемента xsl:sort достаточно очевиден. В нем используются два атрибута: атрибут order - способ сортировки (по возрастанию или по убыванию) и атрибут select - имя поля, по которому производится сортировка. Если нам нужно отсортировать по первому элементу, как в данном примере, то вместо "dogName" можно было поставить точку - ".", для других элементов нужно указывать его имя, например "dogColor", если нам нужно отсортировать записи по цвету собаки. На самом деле атрибутов может быть пять - select, lang, data-type, order и case-order, но мы не будем здесь рассматривать все эти атрибуты, поскольку здесь мы не преследуем цель дать полное описание всех элементов, используемых в XSL, и их атрибутов.
Таблица результатов приведена ниже.
Кличка |
Вес |
Цвет |
Бобик |
2 кг |
бело-серый |
Трезор |
25 кг |
черный |
Тузик |
10 кг |
белый с черными пятнами |
Шарик |
18 кг |
рыжий с черными подпалинами |
С использованием нового синтаксиса легко сменить сортировку по возрастанию на сортировку по убыванию (ex03-8.xml, ex03-8.xsl). Этот пример работает только под управлением XML-парсера версии 3.
Разница заключается в одной строке
<xsl:sort order="descending" select="dogName"/>
Мы изменили значение атрибут order - значение ascending заменено на descending.
Таблица результатов приведена ниже.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Тузик |
10 кг |
белый с черными пятнами |
Трезор |
25 кг |
черный |
Бобик |
2 кг |
бело-серый |
Покажем теперь сортировку по нескольким полям (ex03-9.xsl). Этот пример работает только под управлением XML-парсера версии 3.
В этом примере у нас фигурируют две строки с элементом xsl:sort.
<xsl:sort order="ascending" select="number(dogWeight)" data-type="number"/>
<xsl:sort order="ascending" select="dogName"/>
Строки вначале сортируются по весу собаки, а затем по их кличкам в алфавитном порядке. Обратите внимание - для того, чтобы сортировка выполнялась в числовой последовательности, в элемент xsl:sort мы добавили атрибут data-type. Таблица результатов приведена ниже.
Кличка |
Вес |
Цвет |
Волчонок |
3 кг |
темно-серый |
Трезор |
10 кг |
черный |
Тузик |
10 кг |
белый с черными пятнами |
Бобик |
18 кг |
бело-серый |
Шарик |
18 кг |
рыжий с черными подпалинами |
Заменив значение атрибута order by на descending, мы легко сгруппируем записи о собаках с одинаковым весом так, что клички будут идти в обратном алфавитном порядке. Соответствующий пример вы легко построите сами.
Кличка |
Вес |
Цвет |
Волчонок |
3 кг |
темно-серый |
Тузик |
10 кг |
белый с черными пятнами |
Трезор |
10 кг |
черный |
Шарик |
18 кг |
рыжий с черными подпалинами |
Бобик |
18 кг |
бело-серый |
Рассмотрим теперь способы фильтрации строк таблицы. Первый пример использует старый синтаксис. В нем условие фильтрации указывается непосредственно в атрибуте select (ex04-1.xml, ex04-1.xsl).
Ниже приведена строка, в которую мы внесли необходимые изменения.
xsl:for-each select="tutorial/enimals/dogs/dog[dogWeight$gt$10] " order-by="number(dogWeight); dogName;">
И таблица результатов.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Трезор |
25 кг |
черный |
Вы видите, что в таблице остались только те собаки, чей вес превышает 10 кг, причем первым стоит Шарик, чей вес меньше.
Все дальнейшие примеры в этом параграфе работают только под управлением XML-парсера версии 3.
Более гибкие возможности нам предоставляет новый синтаксис (ex04-2.xsl). Обратите внимание - в новом синтаксисе атрибут order-by в элементе xsl:for-each не поддерживается, вместо него мы вставили два элемента xsl:sort.
<xsl:sort order="ascending" select="number(dogWeight)"/>
<xsl:sort order="ascending" select="dogName"/>
Кроме того, условие фильтра у нас вынесено в отдельный элемент xsl:if.
<xsl:if test="dogWeight>10">
Не забывайте указывать конечный тег элемента xsl:if.
<xsl:if test="dogWeight>10">
<tr bgcolor="#F5F5F5">
<td><xsl:value-of select="dogName"/></td>
<td align="right"><xsl:value-of select="dogWeight"/> <xsl:value-of select="dogWeight/@caption"/></td>
<td><xsl:value-of select="dogColor"/></td>
</tr>
</xsl:if>
В этом примере таблица результатов полностью аналогична предыдущей.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Трезор |
25 кг |
черный |
Полностью преимущества нового синтаксиса проявляются при использовании функций.
Рассмотрим следующий пример (ex04-3.xml, ex04-3.xsl). В этом примере используется функция position(), определяющая порядковый номер фрагмента в исходном XML-файле.
Соответствующий элемент xsl:if.
<xsl:if test="position()<3">
Результат.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Тузик |
10 кг |
белый с черными пятнами |
Продемонстрируем теперь использование более интересных функций - start-with(string,startSubstring) и contains(string,anySubstring). Функция start-with(string,startSubstring) проверяет, начинается ли строка string с подстроки startSubstring. Пример - ex04-4.xml, ex04-4.xsl).
Синтаксис элемента xsl:if.
<xsl:if test="starts-with($varDogName,$varStartWith)">
В этом элементе мы использовали переменные. Значения переменных были инициализированы ранее
<xsl:variable name="varStartWith"><xsl:value-of select="//letter"/></xsl:variable>
<xsl:for-each select="tutorial/enimals/dogs/dog">
<xsl:variable name="varDogName"><xsl:value-of select="dogName"/></xsl:variable>
Переменная varStartWith
представляет собой подстроку, с которой должны начинаться требуемые нам клички. Она не меняется, поэтому инициализируется перед циклом. Переменная
varDogName содержит кличку собаки, она меняется на каждом шаге цикла и, соответственно, инициализируется в теле цикла.
Элемент letter XML-файла содержит букву "Т".
Замечание.
К сожалению, мне не удалось найти другого способа переправить в функцию start-with значения элементов XML-файла. С этим еще как-то можно смириться. Но точно так же не удалось использовать русские буквы в XSL-файлах, поэтому подстроку для сравнения пришлось формировать очень искусственным способом, извлекая ее из XML-файла. С этим смириться трудно, поэтому если мне удастся найти более элегантный способ, я изменю пример, а если такой способ найдет кто-то из читателей, - напишите, пожалуйста, мне об этом. Хотя в реальных примерах подобные подстроки обычно вводятся пользователем в формах поиска, поэтому, возможно, найденный способ вполне приемлем для практических нужд.
Результат.
Кличка |
Вес |
Цвет |
Тузик |
10 кг |
белый с черными пятнами |
Трезор |
25 кг |
черный |
Функция contains(string,anySubstring) проверяет, содержит ли строка string подстроку anySubstring. Пример - ex04-5.xml, ex04-5.xsl.
Синтаксис элемента xsl:if.
<xsl:if test="contains($varDogName,$varStartWith)">
Этот пример полностью аналогичен предыдущему. Элемент letter XML-файла содержит букву "о".
Результат.
Кличка |
Вес |
Цвет |
Бобик |
2 кг |
бело-серый |
Трезор |
25 кг |
черный |
Два элемента xsl:if, вложенные друг в друга, дают нам эффект оператора AND (ex04-6.xsl).
Соответствующий фрагмент XSL-файла.
<xsl:if test="dogWeight>10">
<xsl:if test="dogWeight<20">
...
</xsl:if>
</xsl:if>
Результат.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Можно добиться и эффекта оператора OR. Для этого нам нужно включить два цикла, в каждом из которых формируется своя выборка (ex04-7.xsl).
Соответствующий фрагмент XSL-файла.
<xsl:for-each select="tutorial/enimals/dogs/dog">
<xsl:sort order="ascending" select="number(dogWeight)"/>
<xsl:if test="dogWeight<10">
<tr bgcolor="#F5F5F5">
<td><xsl:value-of select="dogName"/></td>
<td align="right"><xsl:value-of select="dogWeight"/> <xsl:value-of select="dogWeight/@caption"/></td>
<td><xsl:value-of select="dogColor"/></td>
</tr>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="tutorial/enimals/dogs/dog">
<xsl:sort order="ascending" select="number(dogWeight)"/>
<xsl:if test="dogWeight>15">
<tr bgcolor="#F5F5F5">
<td><xsl:value-of select="dogName"/></td>
<td align="right"><xsl:value-of select="dogWeight"/> <xsl:value-of select="dogWeight/@caption"/></td>
<td><xsl:value-of select="dogColor"/></td>
</tr>
</xsl:if>
</xsl:for-each>
Результат.
Кличка |
Вес |
Цвет |
Бобик |
2 кг |
бело-серый |
Шарик |
18 кг |
рыжий с черными подпалинами |
Трезор |
25 кг |
черный |
Если сортировка не требуется, то можно вставить два элемента xsl:if в один элемент xsl:for-each.
Элемент xsl:if можно применять не только для фильтрации строк выборки. Очевидно, что он может быть полезен и во многих других областях. В этом параграфе мы разберем пример использования элемента xsl:if для улучшения внешнего вида таблицы. Заодно мы продемонстрируем реальное использование функции position(). Мы будем использовать эту функцию для того, чтобы чередовать цвет четных и нечетных строк таблицы (ex04-8.xml, ex04-8.xsl).
Фрагмент XSL-файла, который отвечает за требуемое чередование.
<tr>
<xsl:if test="position() mod 2 = 0">
<xsl:attribute name="bgcolor">#CCCCCC</xsl:attribute>
</xsl:if>
С элементом xsl:if и с функцией position() мы уже знакомы. Оператор mod дает нам остаток от деления на 2. А элемент xsl:attribute позволяет нам динамически подставлять в файл результатов различные атрибуты. Это очень мощный элемент, мы разберем еще одно применение этого элемента в следующем параграфе. А сейчас приведем для полноты картины таблицу результатов.
Кличка |
Вес |
Цвет |
Шарик |
18 кг |
рыжий с черными подпалинами |
Тузик |
10 кг |
белый с черными пятнами |
Бобик |
2 кг |
бело-серый |
Трезор |
25 кг |
черный |
Предположим теперь, что в каждой строке таблицы нам нужно сделать ссылку на некоторую страницу и передать на эту страницу два параметра - кличку и вес собаки. Понятно, что для каждой строки эти параметры - свои, и их нельзя прописать явно в XSL-файл. Тем не менее задача легко решается при помощи элемента xsl:attribute.
Мы не будем здесь строить специальный пример, ограничимся только соответствующим фрагментом XSL-файла.
<td>
<!-- Create reference to display details. Parameters - Dog Name and Dog Weight -->
<a target="_blank">
<xsl:attribute name="href">DisplayDetails.html?dogName=<xsl:value-of select="dogName"/>&dogWeight=<xsl:value-of select="dogWeight"/></xsl:attribute>
<xsl:attribute name="title">To view some more details about <xsl:value-of select="dogName"/> click to dog name</xsl:attribute>
<xsl:value-of select="dogName"/>
</a>
</td>
В этом примере в ячейке таблицы мы размещаем ссылку на страницу с подробными описаниями. Ссылка указывается в атрибуте href тега <a>. Поскольку на страницу передаются два параметра, значения которых берутся из XML-файла, этот атрибут формируется динамически. Обратите также внимание - символ & (амперсанд), разделяющий передаваемые параметры, записывается в XSL-файле в виде &. Во втором атрибуте нам нужна всплывающая подсказка (атрибут title), которая появляется при наведении курсора мыши на ссылку. Текст этой подсказки тоже меняется динамически. Наконец, статический атрибут target мы разместили непосредственно в теге <a>.
И, наконец, мы ознакомились с комментариями в XSL-файлах. Это вторая строка приведенного фрагмента.
<!-- Create reference to display details. Parameters - Dog Name and Dog Weight -->
На этом мы завершим рассмотрение возможностей чистого XSLT и перейдем к последнему параграфу в этом документе - к динамическому изменению содержимого Web-страницы при помощи возможностей JavaScript и XML/XSLT без каких-либо дополнительных обращений к базе данных.
Объединим теперь наши знания XML с возможностями, которые нам предоставляет JavaScript. Предположим, что нам нужно иметь возможность динамически изменять сортировку столбцов таблицы при щелчке на заголовке того или иного столбца. Понятно, что для этого нам нужно иметь один XML-файл, содержащий строки таблицы, несколько XSL-файлов, каждый из которых содержит требуемую сортировку и нечто, что объединит это все вместе и заставит работать.
Перейдем к реализации этой программы.
В качестве XML-файла возьмем привычный нам файл со списком собак - ex05-1.xml. Обратите внимание - мы убрали из файла ссылку на XSL-файл - нам нужно менять шаблон преобразования динамически.
Создадим также три XSL-файла, в каждом из которых у нас будет свой элемент xsl:sort, задающий сортировку строк - ex05-1a.xsl, ex05-1c.xsl.
Приведем здесь текст элемента xsl:sort для каждого файла
<xsl:sort order="ascending" select="dogName"/>
<xsl:sort order="ascending" select="number(dogWeight)" data-type="number"/>
<xsl:sort order="ascending" select="dogColor"/>
Теперь нам осталось только объединить все это вместе. Ниже мы полностью приводим текст файла ex05-1.htm, сопроводив его необходимыми комментариями.
<html>
<head>
<script language="JavaScript">
var source;
var style;
Функция инициализации необходимых объектов. В этой же функции выводится первоначальный вариант на экран.
function init() {
Создаем объект для файла - источника данных.
source = new ActiveXObject("Microsoft.XMLDOM");
source.async = false;
Создаем объект для файла с шаблоном преобразования (для файла стиля).
style = new ActiveXObject("Microsoft.XMLDOM");
style.async = false;
Загружаем записи в файл - источник данных.Записи берем из существующего XML-файла.
source.load("ex05-1.xml");
Загружаем файл стиля. Первоначальная сортировка - по цвету.
style.load("ex05-1a.xsl");
Теперь нам нужно вывести информацию на экран. Внимательно проанализируйте синтаксис и запомните его.
document.all.item("xslresult").innerHTML = source.transformNode(style);
return true;
}
Сортируем записи по кличке.
function orderByNick() {
style.load("ex05-1a.xsl");
document.all.item("xslresult").innerHTML = source.transformNode(style);
return true;
}
Сортируем записи по весу.
function orderByWeight() {
style.load("ex05-1b.xsl");
document.all.item("xslresult").innerHTML = source.transformNode(style);
return true;
}
Сортируем записи по цвету.
function orderByColor() {
style.load("ex05-1c.xsl");
document.all.item("xslresult").innerHTML = source.transformNode(style);
return true;
}
</script>
</head>
При загрузке страницы создадим все необходимые объекты и выведем первоначальный вариант на экран.
<body onLoad="init()">
<div id="xslresult">
<!-- Здесь будет размещаться окончательный вариант HTML-содержимого -->
</div>
</body>
</html>
Мы добились своей цели - при щелчке мышью на заголовке столбца строки сортируются в соответствии со значениями в выбранном столбце.
<>
В заключение приведем реальный пример из складской системы. По своим функциональным возможностям этот пример полностью аналогичен предыдущему, детали только в реализации JavaScript-функций и в конкретных данных.
Основная страница - Mgr.html. Эта страница содержит два фрейма - MgrTop.html (страница управления, содержащая все JavaScript-функции) и MgrMain.html - страница-пустышка, в которую в дальнейшем подставляется результат преобразования XML-файла. Страница данных - MgrMainXml.xml. Эти данные получены в результате запроса к реальной базе данных. Для разработчиков на Cache приведем текст CSP-страницы, которая служит источником данных - MgrMainXml.csp. Мы пошли на некоторые ухищрения и вместо реальных страниц MgrTop.html и MgrMainXml.csp подгружаем их копии с расширением *.txt для того, чтобы в браузере можно было увидеть непосредственно исходный код страницы. Сами страницы MgrTop.html и MgrMainXml.csp тоже присутствуют в соответствующей директории, при этом MgrTop.html работает в нашем примере, а MgrMainXml.csp, естественно, бесполезна без Cache-сервера.
На этом наше введение в XML-XSLT заканчивается.
В процессе работы над этим документом использовались примеры Microsoft и примеры с сайта http://www.zvon.org. Рекомендую всем, кто интересуется Web-технологиями, посетить этот сайт. Вы найдете там полные руководства и большое количество примеров по HTML, CSS, различным аспектам XML и т.п. Все материалы на английском языке. Многие документы, например, XSLT Reference можно скачать в виде архива и держать под рукой.
Рекомендую также русскоязычный перевод спецификации "Язык преобразований XSL 1.0" (XSL Transformations 1.0), расположенный по адресу http://www.online.ru/it/helpdesk/xslt01.htm.
Надеемся, что информации, приведенной в этом документе в совокупности с вашей фантазией и XSLT Reference, вам будет достаточно для воплощения в жизнь самых смелых замыслов.