3. Стилевые таблицы XSL

В предыдущем разделе для вывода элементов XML- документа на экран броузера мы применяли Java Script-сценарии Однако, как уже отмечалось, для этих целей предпочтительней использование специально предназначенного для этого средства - стилевых таблиц XSL(Extensible Stylesheet Language).

Стилевыми таблицами (стилевыми листами) принято называть специальные инструкции, управляющие процессом отображения элемента в окне программы-клиента(например, в окне броузера). Предложенные в качестве рекомендация W3C, каскадные стилевые таблицы(CSS- Cascading Style Sheets [7]) уже больше года используются Web- разработчиками для оформления Web- страниц. Поддержка CSS наиболее известными на сегодняшний день броузерами Netscape Navigator(начиная с версии 4.0) и Microsoft Explorer(начиная с версии 3.0), позволила использовать стилевые таблицы для решения самого широкого спектра задач - от оформления домашней странички до создания крупного корпоративного Web-узла. Слово каскадные в определении CSS означает возможность объединения отдельных элементов форматирования путем вложенных описаний стиля. Например, атрибуты текста, заданные в тэге <body>, будут распространяться на вложенные тэги до тех пор, пока в них не встретятся стилевые описания, отменяющие или дополняющие текущие параметры. Таким образом, использование таблиц CSS в HTML было весьма эффективно - отпадала необходимость явного задания тэгов форматирования для каждого из элементов документа.

Являясь очень мощным средством оформления HTML- страниц, CSS- таблицы, тем не менее, не могут применяться в XML-документах, т.к. набор тэгов в этом языке не ограничен и использование статических ссылок на форматируемые объекты документа в этом случае невозможно.

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

Некоторые его отличия от CSS:
Во-первых, стилевые таблицы XSL позволяют определять оформление элемента в зависимости от его месторасположения внутри документа, т.е. к двум элементам с одинаковым названием могут применяться различные правила форматирования.
Во-вторых, языком, лежащем в основе XSL, является XML, а это означает, что XSL более гибок, универсален и у разработчиков появляется возможность использования средства для контроля за корректностью составления таких стилевых списков(используя DTD или схемы данных)
В-третьих, таблицы XSL не являются каскадными, подобно CSS, т.к. чрезвычайно сложно обеспечить "каскадируемость" стилевых описаний, или, другими словами, возможность объединения отдельных элементов форматирования путем вложенных описаний стиля, в ситуации, когда структура выходного документа заранее неизвестна и он создается в процессе самого разбора. Однако в XSL существует возможность задавать правила для стилей, при помощи которых можно изменять свойства стилевого оформления, что позволяет использовать довольно сложные приемы форматирования

В настоящий момент язык XSL находится на стадии разработки в W3C[3] и в будущем, видимо, станет частью стандарта XML. Это означает, что использование этого механизма является наиболее перспективным способом оформления XML- документов. В текущем рабочем варианте W3C, XSL рассматривается не только как язык разметки, определяющий стилевые таблицы - в него заложены средства, необходимые для выполнения действий по фильтрации информации, выводимой в окно клиента, поиска элементов, сложного поиска, основанного на зависимостях между элементами и т.д. На сегодняшний день единственным броузером, поддерживающим некоторые из этих возможностей, является бэта-версия Internet Explorer 5.0, однако в самом ближайшем будущем, безусловно, XSL будет использоваться также широко, как сегодня стандартные тэги HTML

В этом разделе мы рассмотрим упрощенную объектную модель XSL- документа, используемую в текущей версии XSL-конвертора Microsoft msxsl, и поэтому информацию, изложенную далее, нельзя считать описанием стандарта языка. Полный рабочий вариант спецификации XSL в последней его редакции доступен на сервере [3].

Все примеры, приводимые далее, могут быть проверены при помощи XSL- конвертора, свободно доступного на странице Mcrosoft [ www.microsoft.com/xml/xsl/ ] С чего начать

Принцип обработки XML- документов стилевыми таблицами заключается в следующем: при разборе XSL-документа программа-анализатор обрабатывает инструкции этого языка и каждому элементу, найденному в XML- дереве ставит в соответствие набор тэгов, определяющих форматирование этого элемента. Другими словами, мы задаем шаблон форматирования для XML- элементов, причем сам этот шаблон может иметь структуру соответствующего фрагмента XML-документа. Инструкции XSL определяют точное месторасположение элемента XML в дереве, поэтому существует возможность применять различные стили оформления к одинаковым элементам, в зависимости от контекста их использования. На рисунке 3.1 представлена схема, иллюстрирующая этот процесс.

Схема обработки XSL-документа

Рис 1

В общем случае, XSL позволяет автору задавать параметры отображения элемента XML, используя любые языки, предназначенные для форматирования - HTML, RTF и т.д. В этом разделе мы будем использовать в качестве такого языка HTML, т.к. документы, созданные при помощи этого языка разметки могут просматриваться любой подходящей программой просмотра Web-страниц Структура XSL- таблиц

Рассмотрим основные структурные элементы XSL, используемые, в частности, в конверторе msxsl, для создания оформления XML-документов. Правила XSL

XSL- документ представляет собой совокупность правил построения, каждое из которых выделено в отдельный блок, ограниченный тэгами <rule> и </rule>;. Правила определяют шаблоны, по которым каждому элементу XML ставится в соответствие последовательность HTML- тэгов, т.е. внутри них содержатся инструкции, определяющие элементы XML- документа и тэги форматирования, применяемые к ним.

Элементы XML, к которым будет применяться форматирование, обозначаются в XSL дескриптором <target-element/>;. Для указания элемента с конкретным названием (название элемента определяется тэгами, его обозначающими), т.е. определения класса элемента, можно использовать атрибут type="<имя_элемента>"

Вот пример простейшего XSL-документа, определяющего форматирование для фрагмента <flower>rose</flower> :

<xsl>
<rule>
<target-element type="flower"/>
<p color="red" font-size="12">
<children/>
</p>
</rule>
</xsl> 

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

Инструкция <target-element> указывает на то, что данное правило определяет элемент. Параметром type="flower" задается название XML-элемента, для которого будет использоваться это правило. Программа-конвертор будет использовать HTML-тэги, помещенные внутри блока <rule></rule> для форматирования XML-элемента, которому "предназначался" текущий блок. В том случае, если для какого-то элемента XML шаблон не определяется, в выходной документ будут добавлены тэги форматирования по умолчанию (например, <DIV></DIV> )

Процесс разбора XSL-правил является рекурсивным, т.е. если у элемента есть дочерние элементы, то программа будет искать определения этих элементов, расположенных "глубже" в дереве документа. Указанием на то, что необходимо повторить процесс разбора XML документа, теперь уже для дочерних элементов, является инструкция <children/>. Дойдя до нее, анализатор выберет из иерархического дерева XML- элементов нужную ветвь и найдет в XSL-шаблонах правила, определяющие форматирование этих нижележащих элементов. В том случае, если вместо <children> мы укажем инструкцию <empty/>;, программа закончит движение по данной ветви и возвратится назад, в родительское правило. При этом текущее правило никакой информации в выходном HTML-документе изменять не будет, т.к. <empty/> в данном случае означает, что содержимое элемента отсутствует.

Если в одном правиле <target-element> используется несколько раз, то инструкции по форматированию будут распространены на все описываемые внутри него XML-элементы, т.е. можно задавать единый шаблон форматирования для нескольких элементов одновременно:

<xsl>
<rule>
<target-element type="item1"/>
<target-element type="item2"/>
<target-element type="item3"/>
<hr>
<children/>
<hr>
</rule>
</xsl> 

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

XML-документ:

<?XML Version="1.0"?>
<documents>
<books>
<book id="Book1">
<title>Макроэномические показатели экономики 
Римской Империи в период ее расцвета</title>
<author>Иван Петров</author>
<date>21.08.98</date>
</book>
<book id="Book2">
<title>Цветоводство и животноводство.
 Практические советы</title>
<author>Петр Сидоров</author>
<date>10.10.98</date>
</book>
</books>
<articles>
<article id="Article1">
<author>Петр Иванов</author>
<title>Влияние повышения тарифов оплаты за телефон 
на продолжительность жизни населения</title>
<date>12.09.98</date>
</article>
</articles>
</documents>

Содержимое XSL-документа:

<xsl>
  <rule>
    <root/>      
    <HTML>       
    <BODY bgcolor="white">
    <center><hr width="80%"/><b>Library</b>
	<hr width="80%"/><br/> 
    <table width="80%" border="2">
    <children/>  
    </table></center>
    </BODY>
    </HTML>	   
  </rule>
    <rule>
	 <element type="book">
	    <target-element type="author"/>
             </element>
            <td align="center">
	    <p color="red" font-size="14"> <b> 
<children/> 
</b></p></td>
    </rule>
    <rule>
	 <element type="article">
	    <target-element type="author"/>
         </element>
            <td align="center">
	    <p color="red" font-size="14"
 font-style="italic"><children/></p></td>
    </rule>
    <rule>
	    <target-element type="book"/>
            <tr><children/></tr>
    </rule>
    <rule>
	    <target-element type="article"/>
            <tr><children/></tr>
    </rule>
    <rule>
	    <target-element/>
            <td align="center"><p><children/></p></td>
    </rule>
    <rule>
	    <target-element type="books"/>
            <tr><td colspan="3" bgcolor="silver"
			 >Books</td></tr>
            <children/>
    </rule>
    <rule>
	    <target-element type="articles"/>
            <tr><td colspan="3" bgcolor="silver"
			 >Articles</td></tr>
            <children/>
    </rule>
</xsl> 
Корневое правило

Разбор любого XSL- документа всегда начинается с правила для корневого элемента, в котором определяется область всего разбираемого XML документа и поэтому тэги форматирования, помещенные сюда, будут действовать на весь документ в целом. Для обозначения корневого правила необходимо включить в него элемент <root/>;. Например, для того, чтобы задать тэг <body> для формируемой нами выходной HTML- страницы, мы можем использовать следующий фрагмент:

<xsl>
<rule>
<root/>
<html><head><title> Flowers</title></head>
<body bgcolor="white" link="blue" alink="darkblue" 
vlink="darckblue">
<children/>
</body>
</html>
</rule>
</xsl>

В этом примере при помощи инструкций <root/> и <children/> мы определили ряд начальных и конечных HTML-тэгов для нашей страницы, между которыми затем в процессе рекурсивного обхода XSL- анализатора будут помещены остальные элемента документа.

В том случае, если мы не определяем правило для корневого элемента, разбор документа начнется с первого правила с инструкцией <target-element>(для оформления же заголовка документа будет использован образец форматирования, применяющийся по умолчанию). Отношения между элементами

Дочерние элементы в XML-документе всегда находятся внутри области, определяемой тэгами родительского по отношению к ним элемента. Для того, чтобы точно указать месторасположение обрабатываемого элемента в дереве XML, в XSL используется дополнительный тэг <element>;. При помощи него можно указать, какие элементы должны предшествовать текущему, а какие - следовать после него. Например, в следующем фрагменте определяется, что форматирование элемента <title> будет зависеть от его месторасположения внутри XML-документа:

<xsl>
<rule>
<element type="journal">
	    <target-element type="title"/>
         	</element>
<center>
<hr width=80%>
<children/>
<hr width=80%>
</center>    
</rule>
<rule>
<element type="article">
	    <target-element type="title"/>
         	</element>
<td align="center"><p color="blue" font-size="14"
 font-style="italic"><children/>
</td>
</rule>
</xsl>

Как видно из примера, если в XML- документе будет найден элемент <title>, являющийся дочерним по отношению к элементу <article> (название статьи), то его форматирование будет несколько отличаться от элемента <title>, расположенного внутри тэгов <journal> Приоритеты правил

В том случае, если внутри XSL- документа встречается несколько правил для одного и того же элемента, то msxsl будет использовать то из них, которое более точно определяет позицию данного элемента. Т.е. если XSL- документ содержит следующие правила:

<rule>
<element type="journal">
	    <target-element type="title"/>
         	</element>
<center>
<hr width=80%>
<children/>
<hr width=80%>
</center>
</rule>
<rule>
	<target-element type="title"/>
<b>
<children/>
</b>    
</rule>

, то при использовании этой стилевой таблицы в случае, когда элемент <title> является потомком <journal>, к нему будет применено первое правило. Для любых же других элементов будет действовать правило без тэга <element>

В общем случае приоритет правил определяется следующим образом (в порядке убывания приоритета):

Использование атрибутов элементов

Применительно к <target-element> и <element> в правилах также могут использоваться специальные элементы <attribute>;, при помощи которых можно уточнять характеристики обрабатываемых элементов, задавая различные инструкции форматирования для одинаковых элементов с различными атрибутами. Указываемые в <attribute> параметры name и value определяют атрибут XML, который должен иметь текущий обрабатываемый элемент. Например, в следующем фрагменте все элементы с атрибутом free_lance ="true" будут выделены в выходном HTML- документе серым цветом

<rule>
<target-element type="author">
 <attribute name="free_lance" value="true">
	</target-element>
<p color="gray">
<children/>
</p>
 </rule>
Фильтрация элементов

Одним из самых мощных средств XSL является возможность сортировки и выборки элементов, выделяемых из общего дерева элементов документа. Для этого используется элемент <select-elements>;, который заменяет <children/> в правилах, определяя те элементы, которые следует обработать в процессе рекурсивного обхода. Например, в следующем примере будут обработаны только элементы <author>:

<rule>
<target-element type=" staff"/>
<div>
<select-elements>
<target-element type = "author"/>
</select-elements>
</div>
</rule>

Элемент <select-elements> сам по себе не определяет шаблон форматирования, он лишь управляет работой анализатора, обозначая, подобно <children/>, "нижележащие" элементы. В приведенном примере элемент <author> должен быть расположен внутри элемента <staff>

Для того, чтобы в шаблоне выделить не только собственные дочерние элементы, но и дочерние элементы потомков, т.е. использовать несколько уровней вложенности, необходимо задать параметр from = "descendants". Если параметр имеет значение "children", что указывает на то, что выбор должен производится из списка собственных дочерних элементов, то атрибут from может опускаться, т.к. "children" является значением по умолчанию. Правила стилей

В отличие от CSS, в XSL невозможно использование каскадных стилевых определений(т.е. нельзя использовать несколько правил для определения стиля одного того же элемента), т.к. такие ограничения вводит рекурсивный алгоритм работы программы - анализатора. Однако использование правил определения стиля(Style Rules) элемента позволяет каким-то образом скомпенсировать этот недостаток.

Для определения правила стилевого оформления необходимо воспользоваться элементом <style-rule>;, который используется точно также, как и <rule>, но инструкции, содержащиеся в нем, никак не влияют на структуру выходного документа. Поэтому все команды внутри этого правила должны описываться в рамках элемента <apply>. Вот как будет выглядеть, например, определение стиля для элемента <flower>rose</flower>;:

<style-rule>
<target-element type ="flower"/>
<apply color ="red"/>
</style-rule> 
<rule>
<target-element type="flower"/>
<div font-style="italic";>
<children/>
</div>
</rule>

Если бы мы не определили правила <rule>, то в выходной документ не было бы помещено никакой информации, т.к. элемент <style-rule> только определяет параметры стилевого оформления, не предпринимая никаких других действий.

Также надо учитывать, что XSL- анализатор использует CSS для определения задаваемого правилами <style-rule> стиля в выходном HTML-документе, тем самым предоставляя нам возможность использования этого мощного средства при оформлении HTML-страниц После обработки приведенного в примере фрагмента в выходной документ будут помещены следующие элементы:

<div style= "font-style: italic; color : red;"
>rose</div>
Еще один пример:

Стили в формате CSS:

issue {font-weight=bold; color=blue;}
.new {font-weight=bold; color=red;}

Фрагмент XSL- документа, позволяющего использовать подобные стилевые определения:

<style-rule>
<target-element type ="issue"/>
<apply color ="blue"/>
</style-rule> 
<style-rule>
<target-element type ="issue">
<attribute name ="class" value ="new" />
</target-element>
<apply color ="red"/>
</style-rule> 
<rule>
<target-element type="issue"/>
<div>
<children/>
</div>
</rule>
Сценарии

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

Для написания сценариев XSL использует специальный скриптовый язык - ECMAScript. Однако в msxsl для этих целей можно применять Microsoft JavaScript,- язык, который объединил в себе некоторые элементы стандартного JavaScript и ECMAScript. Вычисление выражений

Наиболее простым примером использования сценариев в XSL -документе является вычисление значений параметров описываемых элементов. Для этого надо просто поставить знак равенства в качестве первого символа параметра, что заставит XSL-процессор вычислить значение выражения(синтаксис этого выражения должен отвечать требованиям JavaScript). Например, после разбора этого правила:

<rule>
<target-element type="header">
<hr width="=100-20+'%'">
<children/>
<hr width="80%">
</rule>

, в выходном документе окажутся следующие инструкции:

<hr width=80%>
...
<hr width=80%>

Очень часто в правилах XSL необходимо использовать атрибуты описываемого в них элемента. Для этого мы должны воспользоваться методом getAttribute(), описанным в объектной модели XML (мы рассматриваем объектную модель XML-документов, предложенную Microsoft, список этих функций приведен в конце раздела). Т.к. каждому элементу XSL доступен указатель на соответствующий ему объект, сценарий может обращаться к внутренним функциям и свойствам этих элементов, при помощи которых и осуществляются необходимые действия.

В следующем фрагменте XML- документа определяется элемент <article>, в котором атрибут src используется для задания адреса файла, содержащего текст статьи.

<articles>
<article src="http://server/pages/article.html">
Bugs report</article>
</articles>

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

<rule>
<target-element type="article">
<a href='=getAttribute("src")'>
<children/>
</a>
</rule>

После обработки этого фрагмента в выходной документ будет помещен элемент:

<a href="http://server/pages/article.html"
>Bugs report</a>
Выполнение инструкций

Другим способом помещения в выходной HTML- документ информации, являющейся результатом выполнения каких-либо операций JavaScript – сценариев является использовнаие инструкции <eval>;:

<rule>
<element type="articles">
<target-element type="article">
</element>
<tr><td><eval>childNumber(this)</eval></td><td>
<children/>
</td><tr>
</rule>

Метод childNumber в данном случае возвращает текущий номер дочернего элемента. Определение функций и глобальных переменных

Аналогично тэгу <SCRIPT> в HTML, элемент <define-script> содержит функции и определения глобальных переменных. Обычно в XSL-документе определяется один элемент <define-script>, расположенный в самом начале.

<xsl>
<define-script>
<![CDATA[
var fontSize=12;
function getColor(elem){
return elem.children.item("color",0).text; 
		// Возвращает содержимое дочернего элемента <color>
}]]>
</define-script>
<rule>
<target-element type = "flower">
<div background-color="=getColor(this)"; 
font-size="=fontSize">
<children/>
</div>
</rule>
</xsl>

Если применить эти правила к такому фрагменту XML- документу:

<xml>
<flower>
rose
<color>red</color> 
</flower>
, то на выходе HTML -документ будет содержать следующие элементы:
<div background-color="red"; font-size="12">

Необходимо отметить, что использование глобальных переменных в некоторых случаях может приводить к серьезным ошибкам, вызванным попытками одновременного к ним доступа. Поэтому рекомендуется использовать такие переменные только в качестве констант. Использование Java Script для HTML

Создавая шаблон HTML-документа, Вы можете указывать в нем практически любые элементы HTML, в том числе и блоки <SCRIPT>, внутри которых можно задавать любые конструкции Java Script, используя для этого область CDATA:

<xsl>
  <rule>
    <root/>
    <HTML>
      <HEAD>
        <SCRIPT LANGUAGE="JSCRIPT"><![CDATA[
var ie4=((navigator.appName=="Microsoft Internet 
Explorer")&&(parseInt(navigator.appVersion)  >= 4 ));
       function msover(){
if (ie4){
  		event.srcElement.style.color="red";
  		event.srcElement.style.cursor = "hand";
  	}
}
       function msout(){
	if (ie4){
  		event.srcElement.style.color="black";
  		event.srcElement.style.cursor = "auto";
  	}
}        ]]></SCRIPT>
      </HEAD>
      <BODY>
        <children/>
      </BODY>
    </HTML>
  </rule>
  <rule>
    <target-element type="chapter"/>
    <DIV id='=tagName + formatNumber
	(childNumber(this),"1")'
         background-color="marron"
    onmouseover='="msover("+ tagName +
	 formatNumber(childNumber(this),"1")+")"'
    onmouseout='="msout("+ tagName +
	 formatNumber(childNumber(this),"1")+")"'
      <children/>
    </DIV>
  </rule>
</xsl>
Если использовать эти правила для следующего XML- документа:
<contents>
  <chapter>Part1</chapter>
  <chapter>Part2</chapter>
  <chapter>Part3</chapter>
</contents>
то в результате мы получим такой HTML-файл:
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JSCRIPT">
var ie4=((navigator.appName=="Microsoft Internet 
Explorer")&&(parseInt(navigator.appVersion)  >= 4 ));
function msover(){
if (ie4){
  		event.srcElement.style.color="red";
  		event.srcElement.style.cursor = "hand";
 	}
}
function msout(){
	if (ie4){
  		event.srcElement.style.color="black";
  		event.srcElement.style.cursor = "auto";
  	}
  }
</SCRIPT>
</HEAD>
<BODY>
<DIV id=''chapter1" onmouseover="msover("chapter1")"'
			  onmouseout="msout("chapter1")"'
      Part 1
       </DIV>
<DIV id=''chapter2" onmouseover="msover("chapter2")"'
			  onmouseout="msout("chapter2")"'
      Part 2
       </DIV>
<DIV id=''chapter3" onmouseover="msover("chapter3")"'
			  onmouseout="msout("chapter3")"'
      Part 3
       </DIV>
</BODY>
</HTML>
Встроенные функции XSL

В завершении приведем список внутренних функций, которые можно использовать в JavaScript –сценариях, предназначенных для анализатора msxsl:
Ancestor(elementType, elem) Возвращает для текущего элемента ссылку на ближайший родительский элемент заданного типа. Если такого элемента нет или текущий элемент пустой, то возвращает null
ChildNumber(elem) Возвращает индекс текущего элемента в списке других дочерних элементов данного типа.
AncestorChildNumber() Возвращает номер ближайшего предка текущего элемента или null, если такового не существует
path(xsl) Возвращает массив, содержащий "путь" к текущему элементу - в каждую ячейку этого массива помещается цифровое значение, указывающее на количество элементов одинакового типа, находящихся на текущем уровне вложенности. Первым значением этого массива будет представлен корневой элемент, последним - текущий. Размер массива определяет глубину вложенности текущего элемента.
HierarchicalNumberRecursive (elementType,elem) Метод, похожий на метод path, но возвращает только дочерние элементы
FormatNumber(n,format) Возвращает строку - символьное представление номера(т.е. "один", "два" и т.д.). Возможно определение следующих форматов:
"1" - 0,1,2,..
"01" - 01,02,03,...
"a" - a,b,c,..z, aa, ab,..zz
"A" - A,..,Z,AA, .. ZZ
FormatNumberList(list,format,separator) Возвращает строку, представляющую список, элементами которого являются символьные представления чисел

Назад | Содержание | Вперед