|
|||||||||||||||||||||||||||||||||||
|
Глава 8Работа с функциями XSLT и XPath В этой главе мы изучим доступные вам в XSLT функции — как встроенные в XSLT, так и функции XPath. Вы уже встречались с этими функциями выборочно (например, с id, generate-id, position, countи т.д). В этой главе мы рассмотрим их все. Некоторые функции входят в XSLT, некоторые пришли из XPath. И XSLT, и XPath обновляются до версии 2.0, и в конце главы я приведу список новых, готовящихся к выпуску функций. (Пока что никаких новых функций версии 2.0 еще не опубликовано.) В XSLT встроены следующие функции: • element-available(); • function-available(); • current(); • document(); • key(); • format-number(); • generate-id(); • system-property(); • unparsed-entity-uri(). Вы также можете пользоваться многими функциями XPath. Я сгруппирую их по типам данных XPath, с которыми они работают. Существует четыре типа данных: наборы узлов, строки, числа и логические значения. С наборами узлов работают следующие функции XPath: • count(); • id(); • last(); • local-name(); • name(); • namespace-uri(); • position(). Следующие функции XPath работают со строками: • concat(); • contains(); • normalize-space(); • starts-with(); • string(); • string-length(); • substring(); • substring-after(); • substring-before(); • translate(). Для работы с числами служат следующие функции XPath: • ceiling(); • floor(); • number(); • round(); • sum(). И эти функции XPath работают с логическими значениями: • boolean(); • false(); • lang(); • not(); • true(). В этой главе мы познакомимся с работой всех вышеназванных функций, а начнем с функций, встроенных в XSLT. Не обязательно читать всю главу подряд — скорее ею лучше пользоваться как справочным руководством: здесь перечислены все функции в удобном для просмотра виде. Функции XSLTВ следующем списке перечислены встроенные в XSLT функции: • current(). Возвращает текущий (current) узел — но не контекстный узел. Текущий узел — это узел, используемый в данный момент в таких циклах, как <xsl:for-each>. Функцию current нельзя применять в образцах; • document(). Позволяет считывать несколько документов; • element-available(). Показывает, доступен ли элемент расширения; • format-number(). Форматирует числа при выводе; • function-available(). Показывает, доступна ли функция расширения; • generate-id(). Указывает процессору XSLT присвоить идентификатор узлу и возвратить его. При повторном применении generate-id к тому же узлу функция возвращает присвоенный ранее идентификатор; • key(). Позволяет осуществлять поиск по ключу; • system-property(). Позволяет проверить три системных свойства: xsl:version(версия XSLT, поддерживаемая процессором XSLT), xsl:vendor(производитель процессора XSLT) и xsl:vendor-url(URL производителя процессора XSLT); • unparsed-entity-uri(). Предоставляет доступ к неразобранным сущностям, объявленным в DTD или схеме, через URI. В следующих разделах все эти функции рассматриваются подробно и с примерами. current()Функция currentвозвращает текущий (current) — не контекстный (context) — узел. Контекстный узел шаблона — это узел в выбранном наборе узлов, к которому применяется шаблон. Текущий узел, с другой стороны, это используемый в данный момент узел в таких циклах, как <xsl:for-each>. Функция возвращает текущий узел как набор узлов с одним узлом: node-set current() Обратите внимание: currentнельзя применять в образцах, поскольку образцы должны быть независимы от пути обработки, а способы реализации таких структур, как циклы, в процессорах XSLT могут различаться. Следующий пример, в котором я выбираю элементы <PLANET>при помощи шаблона (листинг 8.1), демонстрирует работу функции current. Внутрь шаблона я поместил элемент <xsl:for-each>, и шаблон применяется только в том случае, если выбранный шаблоном контекстный узел в то же время является и текущим узлом в очередной итерации в элементе <xsl:for-each>. Листинг 8.1. Применение функции current <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . <TABLE BORDER=2> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:for-each select="/PLANETS/*[.=current()]"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:for-each> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Результат применения этой таблицы стилей будет точно таким же, как если бы элемента <xsl:for-each>вообще не было, потому что тело элемента <xsl:for-each>применяется только тогда, когда контекстный узел совпадает с текущим узлом. document()Функция documentособенно полезна, потому что с ее помощью можно читать несколько документов и обрабатывать их содержимое. Функция documentприменяется следующим образом: node-set document(uri, base-uri?) Функция принимает параметр uri, который может представлять собой URI читаемого документа или набор узлов, чьи строковые значения задают идентификаторы URI. Второй, необязательный параметр base-uriпредставляет собой набор узлов, базовый URI которого используется для вычисления всех относительных URI, могущих находиться в параметре uri. В следующем примере (листинг 8.2) я обрабатываю в процессоре XSLT один документ, planets1.xml, и также считываю и обрабатываю второй документ, planets2.xml. Вот planets1.xml. Листинг 8.2. planets1.xml <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS href="planets2.xml"> </PLANETS> Вот planets2.xml— в элементе <PLANET>содержится одна планета (листинг 8.3). Листинг 8.3. planets2.xml <?xml version="1.0"?> <PLANET> <NAME>Mercury</NAME> <MASS UNITS="(Earth = 1)">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> А вот таблица стилей planets.xml, которую я применяю к planets1.xml. Таблица стилей содержит шаблон, выбирающий элемент <PLANET>в planets1.xml, и в этом шаблоне я считываю planets2.xmlпри помощи <xsl:apply-templates>и функции document(листинг 8.4). Листинг 8.4. Применение функции document <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates select="document(@href)"/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> . . . <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Эта таблица стилей обрабатывает данные в planets1.xmlи также считывает и обрабатывает planets2.xml; вот полный результат — как видите, данные для planets2.xmlдобавились нужным образом: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> </TABLE> </BODY> </HTML> Функция documentудобна, поскольку позволяет считывать на этапе выполнения такие дополнительные документы, как справка о правах владения или отказе от них, фирменный бланк и т.п. element-available()Функция element-available()используются для определения доступности определенного элемента расширения. Ее применяют следующим образом: boolean element-available(element-name) Функция принимает имя искомого элемента и, если элемент доступен, возвращает истину, если недоступен — ложь. Мы уже встречались с этой функцией в главе 5. В примере element-availableтой главы я проверял наличие элемента <starpowder:calculate>таким образом: <xsl:choose xmlns:starpowder="http://www.starpowder.com"> <xsl:when test="element-available('starpowder:calculate')"> <starpowder:calculate xsl:extension-element-prefixes="starpowder"/> </xsl:when> <xsl:otherwise> <xsl:text>Sorry, can't do math today.</xsl:text> </xsl:otherwise> </xsl:choose> format-number()Как можно догадаться по имени, функция format-number()служит для форматирования чисел с преобразованием их в строки. Она применяется так: string format-number(number, format, name?) Функция возвращает форматируемое число в виде строки. Функции передаются число number, которое нужно отформатировать, строка форматирования formatи необязательный параметр-строка name. Строка name — это имя QName, задающее формат так, как он создается элементом <xsl:decimal-format>(который будет рассмотрен в конце этой главы). Форматирующая строка formatдолжна соответствовать соглашениям класса Java DecimalFormat.
Форматирующая строка (format string) состоит из следующих частей: • format-string:= subpattern (;subpattern)? • subpattern:= prefix? integer (.fraction)?suffix? • prefix:= [#x0..#xFFFD] - specialCharacters • suffix:= [#x0..#xFFFD] - specialCharacters • integer:= '#'* '0'* '0' • fraction:= '0'* '#'* Далее показаны специальные символы ( specialCharacters), которые можно использовать в подчиненных образцах, subpattern(эти символы можно изменить при помощи элемента <xsl:decimal-format>, который будет рассмотрен в конце этой главы): • 0— на этом месте всегда должна стоять цифра; • #— цифра, если только это не избыточный лидирующий или завершающий ноль; • .— разделитель десятичной части; • ,— разделитель групп разрядов; • ;— разделяет форматы; • -— знак минуса; • %— умножить на 100 и показать как проценты; • ‰— умножить на 1000 и показать в тысячных частях; • Е— разделяет мантиссу и экспоненциальную часть; • ¤— символ валюты (#xA4); • '— заключает в кавычки специальные символы. Следующий пример (листинг 8.5) демонстрирует работу функции. Я форматирую значения из planets.xml, отображаемые в таблице HTML. Листинг 8.5. Форматирование чисел <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> . . . </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="format-number(., '#.###')"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="format-number(., '#.###')"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="format-number(., '###.#')"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот результат, в котором выведены отформатированные числа: <HTML> <HEAD> <TITLE> The Formatted Planets Table </TITLE> </HEAD> <BODY> <H1> The Formatted Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>0.055 (Earth = 1)</TD> <TD>1.516 miles</TD> <TD>58.65 days</TD> </TR> <TR> <TD>Venus</TD> <TD>0.815 (Earth = 1)</TD> <TD>3.716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2.107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> Этот результирующий документ показан на рис. 8.1. (MSXML3 и Saxon отбрасывают лидирующие нули, поэтому 0.055 выводится как .055 и т.д.) Рис. 8.1. Форматирование чисел при помощи XSLT Следующие примеры демонстрируют способы применения форматирующих строк. Заметьте, что при помощи точки с запятой (;) можно отделять образцы для положительных и отрицательных чисел.
function-available()При помощи функции XSLT 1.0 function-availableможно проверить наличие функции расширения: boolean function-available(function-name) Функция принимает в качестве параметра имя искомой функции и, если функция доступна, возвращает истину, если нет — ложь. Следующий пример мы уже рассматривали в главе 5. В этом случае я хочу выполнить ряд математических операций при помощи функции расширения starpowder:calculate, и если она отсутствует, вывести в результирующий документ текст «Sorry, can't do math today.» (Извините, сегодня математические вычисления не работают.). Можно, конечно, также прекратить обработку и вывести сообщение об ошибке при помощи элемента <xsl:message>: <xsl:choose xmlns:starpowder="http://www.starpowder.com"> <xsl:when test="function-available('starpowder:calculate')"> <xsl:value-of select="starpowder:calculate('2+2')"/> </xsl:when> <xsl:otherwise> <xsl:text>Sorry, can't do math today.</xsl:text> </xsl:otherwise> </xsl:choose> generate-id()Функция generate-idдает указание процессору XSLT присвоить узлу идентификатор (который возвращается как строка). Функция применяется следующим образом: string generate-id(node) Функции следует передать набор узлов, содержащий только узел, которому нужен идентификатор (все узлы после первого игнорируются); функция возвращает уникальный идентификатор для этого узла. Если передать функции снова этот же узел, она вернет тот же идентификатор. Заметьте, что идентификаторы различаются в зависимости от процессора XSLT. Следующий пример взят из главы 6, но сейчас я собираюсь добавить в planets.htmlоглавление, составленное из гиперссылок. Для создания оглавления я прохожу в цикле по всем планетам при помощи элемента <xsl:for-each>. На каждом шаге цикла для текущей планеты я создаю гиперссылку и создаю атрибут HREFсо значением, равным уникальному идентификатору, при помощи шаблона значений атрибута: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <xsl:for-each select="PLANET"> <H2><A HREF="#{generate-id()}"> <xsl:value-of select="NAME"/>
<P/> </xsl:for-each> . . . Этот код добавляет каждой планете идентификатор и создает нужные гиперссылки. Я могу создать закладки гиперссылки в HTML-таблице данных планет, установив по очереди атрибут <NAME>закладки в идентификатор для каждого элемента <PLANET>так, чтобы он стал гиперссылкой-назначением: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR>
<xsl:value-of select="NAME"/> </A></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> . . . <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот и все; сейчас я создал гиперссылки с атрибутом HREF, значение которого равно идентификатору элемента <PLANET>; при помощи же этого идентификатора я сделал каждый элемент <PLANET>назначением гиперссылки. Когда пользователь щелкает на гиперссылку в оглавлении, браузер прокручивает данные до соответствующей записи планеты в HTML-таблице. Вот как выглядит результирующий документ при использовании процессора Xalan: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <H2> <A href="#N5">Mercury</A> </H2> <P></P> <H2> <A href="#N20">Venus</A> </H2> <P></P> <H2> <A href="#N3B">Earth</A> </H2> <P></P> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD><A NAME="N5">Mercury</A></TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> <TR> <TD><A NAME="N20">Venus</A></TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD><A NAME="N3B">Earth</A></TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> key()Функция keyслужит для поиска узлов с определенным значением по именованному ключу, ее следует применять следующим образом: node-set key(name, value) Функции передаются имя ключа nameв виде строки и требуемое значение ключа value, которое нужно найти. Функция возвращает набор узлов из найденных узлов. Ключи создаются при помощи элемента <xsl:key>. Мы уже видели следующий пример в главе 4 — здесь я при помощи ключей выбираю планеты, чей атрибут COLORустановлен в « BLUE»: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> . . . <PLANET COLOR="BLUE"> <NAME>Earth</NAME> <MASS UNITS>"(Earth = 1)">1</MASS> <DAY UNITS="days">1</DAY> <RADIUS UNITS="miles">2107</RADIUS> <DENSITY UNITS="(Earth = 1)">1</DENSITY> <DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии--> </PLANET> </PLANETS> Теперь при помощи <xsl:key>я могу создать ключ COLOR, выбирающий элементы <PLANET>и проверяющий их атрибут COLOR. Вот как он выглядит: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="COLOR" match="PLANET" use="@COLOR"/> . . . Теперь я могу применить образец « key()» для выбора элементов <PLANET>с атрибутом COLORсо значением « BLUE» таким образом: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="COLOR" match="PLANET" use="@COLOR"/> <xsl:template match="/PLANETS"> <HTML> . . . <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates select="key('COLOR', 'BLUE')"/> </TABLE> </BODY> </HTML> </xsl:template> . . . И вот результат — как видите, единственной планетой, удовлетворившей образцу, была Земля: <HTML> <НЕАD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> system-property()Функция system-propertyвозвращает значение нескольких системных свойств в виде строк; вот как ей следует пользоваться: string system-property(property) Можно проверять следующие возможные значения системного свойства property: • xsl:version. Возвращает версию XSLT; • xsl:vendor. Возвращает строку, идентифицирующую производителя процессора XSLT; • xsl:vendor-url. Возвращает URL производителя процессора XSLT. Вот пример — версию XSLT можно проверить, вызвав system-property('xsl:version'); <?xml version="1.0"?> <xsl:stylesheet version="2.0" xmlns:xsl="http.//www.w3.org/1999/XSL/Transform"> . . . <xsl:if text="system-property('xsl:version')=2.0"> <xsl:namespace name="starpowder"/> </xsl:if> . . . </xsl:stylesheet> Проверка версии XSLT может понадобиться, если вы хотите воспользоваться возможностями, которые появились в последних версиях. unparsed-entity-uri()Функция unparsed-entity-uri()предоставляет доступ к объявлениям неразобранных сущностей в DTD или схеме исходного документа. Неразобранная сущность (unparsed entity) — это обычно двоичные данные, такие как файл с рисунком. Функция применяется следующим образом: string unparsed-entity-uri(name) В функцию передается имя name неразобранной сущности, и функция возвращает для нее идентификатор. В следующем примере (листинг 8.6) я добавил в planets.xmlDTD и объявил три неразобранных сущности, соответствующих рисункам планет — image1, image2и image3, — и обратился к ним, добавив в каждый элемент <PLANET>атрибут IMAGE. Листинг 8.6. planets.xml с неразобранными сущностями <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <!DOCTYPE PLANETS [ <!ELEMENT PLANET (CUSTOMER)*> <!ELEMENT CUSTOMER (NAME,MASS,RADIUS,DAY)> <!ELEMENT NAME (#PCDATA)> <!ELEMENT MASS (#PCDATA)> <!ELEMENT RADIUS (#PCDATA)> <!ELEMENT DAY (#PCDATA)> <!ENTITY image1 SYSTEM "http://starpowder.com/image1.gif" NDATA GIF> <!ENTITY image2 SYSTEM "http://starpowder.com/image2.gif" NDATA GIF> <!ENTITY image3 SYSTEM "http://starpowder.com/image3.gif" NDATA GIF> ]> <PLANETS> <PLANET IMAGE="image1"> <NAME>Mercury</NAME> <MASS UNITS="(Earth = 1)">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> <PLANET IMAGE="image2"> <NAME>Venus</NAME> <MASS UNITS="(Earth = 1)">.815</MASS> <DAY UNITS="days">116.75</DAY> <RADIUS UNITS="miles">3716</RADIUS> <DENSITY UNITS="(Earth = 1)">.943</DENSITY> <DISTANCE UNITS="million miles">66.8</DISTANCE><!--В перигелии--> </PLANET> <PLANET IMAGE="image3"> <NAME>Earth</NAME> <MASS UNITS="(Earth = 1)">1</MASS> <DAY UNITS="days">1</DAY> <RADIUS UNITS="miles">2107</RADIUS> <DENSITY UNITS="(Earth = 1)">1</DENSITY> <DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии-> </PLANET> </PLANETS> (Заметьте, что некоторые процессоры XSLT основаны на разборщиках XML, которые проверяют, могут ли они найти сущность по заданному URI, — потому, если вы хотите опробовать этот пример, вставьте URI каких-либо настоящих рисунков.) Теперь я могу извлечь URI рисунков в таблице стилей при помощи функции unparsed-entity-uriи создать элементы HTML <IMG>, применив эту функцию в результирующем документе (листинг 8.7). Листинг 8.7. Применение unparsed-entity-uri <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/> <IMG SRC="{unparsed-entity-uri(@IMAGE)}"/> </TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> . . . <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> И вот результирующий документ: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury<IMG SRC="http://starpowder.com/image1.gif"></TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> <TR> <TD>Venus<IMG SRC="http://starpowder.com/image2.gif"></TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD>Earth<IMG SRC="http://starpowder.com/image3.gif"></TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> На этом мы заканчиваем рассмотрение функций XSLT. Я перехожу к функциям XPath, начиная с функций для работы с наборами узлов. Функции XPath для работы с наборами узловСледующие функции XPath работают с наборами узлов: • count(node-set). Возвращает число узлов в наборе узлов; • id(string ID). Возвращает набор узлов элемента, ID которого совпадает с переданной функции строкой, или пустой набор узлов, если таковых элементов не оказалось; • last(). Возвращает число узлов в наборе узлов; • local-name(node-set). Возвращает локальное имя первого узла в наборе узлов; • name(node-set). Возвращает полное имя первого узла в наборе узлов; • namespace-uri(node-set). Возвращает URI пространства имен первого узла в наборе узлов; • position(). Возвращает позицию контекстного узла в контекстном наборе узлов, начиная с 1. count()Функция countподсчитывает число узлов в наборе узлов number count(node-set) Функция принимает набор узлов и возвращает количество узлов в этом наборе. Следующий пример применения функции countмы уже рассматривали в главе 6. В этом случае набор узлов образован из всех элементов <PLANET>в planets.xml; я получил его при помощи пути расположения « //PLANET»: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="*"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="PLANET"> <xsl:copy use-attribute-sets="numbering"> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:attribute-set name="numbering"> <xsl:attribute name="number"><xsl:number/></xsl:attribute> <xsl:attribute name="total"> <xsl:value-of select="count(//PLANET)"/> </xsl:attribute> </xsl:attribute-set> </xsl:stylesheet> Заметьте, что в приведенном ниже результате каждый элемент <PLANET>обладает и атрибутом number, и атрибутом total, а атрибут totalхранит общее количество элементов <PLANET>, которое было найдено при помощи count: <?xml version="1.0" encoding="UTF-8"?> <PLANETS> <PLANET number="1" total="3"> <NAME>Mercury</NAME> <MASS>.0553</MASS> <DAY>58.65</DAY> <RADIUS>1516</RADIUS> <DENSITY>.983</DENSITY> <DISTANCE>43.4</DISTANCE> </PLANET> <PLANET number="2" total="3"> <NAME>Venus</NAME> <MASS>.815</MASS> <DAY>116.75</DAY> <RADIUS>3716</RADIUS> <DENSITY>.943</DENSITY> <DISTANCE>66.8</DISTANCE> </PLANET> <PLANET number="3" total="3"> <NAME>Earth</NAME> <MASS>1</MASS> <DAY>1</DAY> <RADIUS>2107</RADIUS> <DENSITY>1</DENSITY> <DISTANCE>128.4</DISTANCE> </PLANET> </PLANETS> id()Функция id возвращает набор узлов, в котором все узлы имеют такой же ID, как и переданный в эту функцию. Функция применяется следующим образом: node-set id(id-value) В функцию передается значение ID, и она возвращает набор узлов с узлами, имеющими этот ID. Заметьте, что вы можете задать несколько ID, разделенных символами-разделителями, и функция вернет набор узлов с элементами, обладающими этими ID. Следующее правило-пример выбирает текст всех элементов с ID «favorite»: <xsl:template match = "id('favorite')"> <H3><xsl:value-of select="."/></H3> </xsl:template> Отметьте также, что вы должны объявлять значения ID как в DTD или схеме. Объявление DTD для planets.xml, объявляющее ID с именем idи присваивающее ему значение «favorite», может выглядеть так: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="stylesheet.xsl"?> <!DOCTYPE PLANETS [ <!ELEMENT PLANET (CUSTOMER)*> <!ELEMENT CUSTOMER (NAME,MASS,RADIUS,DAY)> <!ELEMENT NAME (#PCDATA)> <!ELEMENT MASS (#PCDATA)> <!ELEMENT RADIUS (#PCDATA)> <!ELEMENT DAY (#PCDATA)> <!ATTLIST PLANET id ID #REQUIRED> ]> <PLANETS> <PLANET id='favorite'> <NAME>Mercury</NAME> <MASS UNITS="(Earth = 1)">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> . . . Заметьте, что некоторые процессоры XSLT не могут осуществлять выборку по ID, потому что они не читают объявления DTD и схему XML. Это одна из задач, которые призван решить XSLT 2.0 — сделать доступной информацию об идентификаторах. last()Функция lastвозвращает число узлов в наборе узлов, поэтому ее значение равно позиции последнего узла. Она применяется следующим образом: number last() Следующий пример из главы 5 последовательно выводит в результирующий документ имена планет. Однако я хочу, чтобы выводилось не просто «The first three planets are: Mercury Venus Earth» (первые три планеты: Меркурий Венера Земля), a «The first three planets are: Mercury, Venus, and Earth.» Необходимые знаки пунктуации можно добавить, определяя элемент, с которым мы работаем, при помощи функции position и проверяя позицию при помощи <xsl:if>: <?xml version="1.0"?> <xsl stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: <xsl:apply-templates select="PLANET"/> </PLANETS> </DOCUMENT> </xsl:template> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> <xsl:if test="position()!=last()">, </xsl:if> <xsl:if test="position()=last()-1">and </xsl:if> <xsl:if test="position()=last()">.</xsl:if> </xsl:template> </xsl:stylesheet> Вот результат: <?xml:version="1.0" encoding="UTF-8"?> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: Mercury, Venus, and Earth. </PLANETS> </DOCUMENT> local-name()Функция local-nameвозвращает локальное (неполное) имя узла. Функция применяется следующим образом: string local-name(node-set?) В функцию передается набор узлов с единственным узлом, и функция возвращает локальное имя этого узла. (Если в наборе узлов больше, чем один узел, берется только первый.) Если не передать никаких узлов, функция вернет локальное имя контекстного узла. В следующем примере я создаю новые элементы при помощи <xsl:element>и определяю имена контекстных узлов при помощи local-name: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <xsl:element name="{local-name(.)}"> <xsl:for-each select="PLANET"> <xsl:element name="{local-name(.)}"> <xsl:for-each select="*"> <xsl:element name="DATA"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet> name()Функция nameаналогична local-name, за тем исключением, что она возвращает полностью определенное имя узла. Вот как следует применять эту функцию: string name(node-set?) namespace-uri()Функция namespace-uriвозвращает строку, содержащую URI пространства имен в расширенном имени узла. Как правило, это URI в объявлении пространства имен, установленное при помощи атрибутов xmlnsили xmlns:prefix. Функция применяется следующим образом (заметьте, что эту функцию можно применять только к элементам или атрибутам, результатом обработки остальных узлов будет пустая строка): string namespace-uri(node-set?) Например, в planets.xmlя могу добавить пространство имен «star» (звезда): <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <star:PLANETS xmlns:star="http://starpowder.com"> <star:PLANET> <star:NAME>Mercury</star:NAME> <star:MASS UNITS="(Earth = 1)">.0553</star:MASS> <star:DAY UNITS="days">58.65</star:DAY> <star:RADIUS UNITS="miles">1516</star:RADIUS> <star:DENSITY UNITS="(Earth = 1)">.983</star:DENSITY> <star:DISTANCE UNITS="million miles">43.4</star:DISTANCE><!--В перигелии--> </star:PLANET> <star:PLANET> <star:NAME>Venus</star:NAME> <star:MASS UNITS="(Earth = 1)">.815</star:MASS> <star:DAY UNITS="days">116.75</star:DAY> <star:RADIUS UNITS="miles">3716</star:RADIUS> <star:DENSITY UNITS="(Earth = 1)">.943</star:DENSITY> <star:DISTANCE UNITS="million miles">66.8</star:DISTANCE><!--В перигелии--> </star:PLANET> . . . И я могу найти URI этого пространства имен в таблице стилей при помощи namespace-uri: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:star="http://www.starpowder.com"> <xsl:template match="/PLANETS"> <xsl:value-of select="namespace-uri()"/> </xsl:template> . . . Вот результат: <?xml version="1.0" encoding="UTF-8"?> http://starpowder.com position()Функция positionвозвращает позицию контекстного узла: number position() С этой функцией мы встречались на протяжении всей книги. Такой пример вы уже видели раньше в этой главе, в нем для создания предложения «The first three planets are: Mercury, Venus, and Earth.» используются функции lastи position: <?xml version="1.0"?> <xsl stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: <xsl:apply-templates select="PLANET"/> </PLANETS> </DOCUMENT> </xsl:template> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> <xsl:if test="position()!=last()">, </xsl:if> <xsl:if test="position()=last()-1">and </xsl:if> <xsl:if test="position()=last()">.</xsl:if> </xsl:template> </xsl:stylesheet> Полученный результат: <?xml:version="1.0" encoding="UTF-8"?> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: Mercury, Venus, and Earth. </PLANETS> </DOCUMENT> Функции XPath для работы со строкамиВ XSLT доступны следующие функции XPath для работы со строками: • concat(string string1, string string2,...). Возвращает конкатенацию (объединение) всех переданных ей строк; • contains(string string1, string string2). Возвращает истину, если первая строка содержит (contains) вторую строку; • normalize-space(string string1). Возвращает строку string1(или контекстный узел, если строки string1нет) после отбрасывания лидирующих и завершающих символов-разделителей и замены нескольких последовательных разделителей на один пробел; • starts-with(string string1, string string2). Возвращает истину, если первая строка начинается (starts with) со второй подстроки; • string(object). Преобразует объект в строку; • string-length(string string1). Возвращает количество символов в строке string1; • substring(string string1, number offset, number length). Возвращает lengthсимволов из строки, начиная со смещения offset; • substring-after(string string1, string string2). Возвращает часть строки string1после первого вхождения string2; • substring-before(string string1, string string2). Возвращает часть строки string1вплоть до первого вхождения строки string2; • translate(string string1, string string2, string string3). Возвращает строку string1, в которой все вхождения символов в строке string2заменены на соответствующие символы в строке string3; В последующих разделах я рассмотрю каждую из этих функций. concat()Функция concatобъединяет вместе все переданные ей строки и возвращает полученную строку: concat(string string1, string string2, ...) В качестве примера рассмотрите разработанный ранее вариант таблицы стилей planets.xsl, отображающей значения элементов и значения атрибутов UNITS, в котором шаблоны применяются следующим образом: <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> Такой код отображает строковое значение, контекстный узел, пробел и строковое значение атрибута UNITS. Но код можно значительно сократить при помощи функции concat(листинг 8.8). Листинг 8.8. Применение функции concat <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="concat(., ' ', @UNITS)"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="concat(., ' ', @UNITS)"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="concat(., ' ', @UNITS)"/> </xsl:template> </xsl:stylesheet> contains()Функция containsпроверяет, содержится ли одна строка внутри другой; если это так, функция возвращает истину, если нет — ложь. Функция применяется следующим образом: boolean contains(container-string, contained-string) Следующий пример взят из главы 7; в этом случае я хочу осуществить поиск слова «miles» во всех атрибутах, и если оно будет найдено, добавить в результирующий документ текст «You should switch to kilometers.» (Нужно перевести в километры.): <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> <TD><xsl:apply-templates select="DISTANCE"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> . . . <xsl:template match="DISTANCE"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="//*[contains(@UNITS, 'miles')]"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:text>You should switch to kilometers.</xsl:text> </xsl:template> </xsl:stylesheet> Вот результирующий документ: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> <TD>Distance</TD> </TR> <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 You should switch to kilometers.</TD> <TD>58.65 days</TD> <TD>43.4 You should switch to kilometers.</TD> </TR> <TR> <TD>Venus</TD> <TD>.815 (Earth = 1)</TD> <TD>3716 You should switch to kilometers.</TD> <TD>116.75 days</TD> <TD>66.8 You should switch to kilometers.</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2107 You should switch to kilometers.</TD> <TD>1 days</TD> <TD>128.4 You should switch to kilometers.</TD> </TR> </TABLE> </BODY> </HTML> normalize-space()Функция normalize-spaceудаляет лидирующие и завершающие символы-разделители и сжимает все внутренние идущие подряд разделители в один пробел, возвращая полученную строку. Функция применяется следующим образом: string normalize-space(string?) В следующем примере я добавил дополнительные пробелы в атрибут UNITSэлемента <MASS>Меркурия: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets xsl"?> <PLANETS> <PLANET> <NAME>Mercury</NAME> <MASS UNITS=" (Earth = 1) ">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> . . . Функция normalize-spaceпоможет удалить лишние пробелы в таблице стилей: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="normalize-space(@UNITS)"/> </xsl:template> . . . И вот результат — обратите внимание на то, что лишние пробелы убраны: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> . . . starts-with()Как можно догадаться по имени (начинается с), функция starts-withпроверяет, начинается ли одна строка с другой. boolean starts-with(string-to-examine, possible-start-string) В этом примере из главы 4 при помощи starts-withвыбираются текстовые узлы, текст в которых начинается с «Е», для того чтобы выбрать Earth (Землю). Затем в описание Земли добавляется текст «(the World)» (мир), и получается «Earth (the World)»: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> . . . </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:apply-templates select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="text()[starts-with(., 'E')]"> <xsl:text>(the World)</xsl:text> </xsl:template> <xsl:template match="NAME"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот результат — заметьте, что подпись для Земли теперь выглядит как «Earth (the World)»: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> . . . <TR> <TD>Earth (the World</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> string()Функция stringпросто преобразует переданный ей объект в строку. Функция применяется следующим образом: string string(object?) Как правило, необходимости в данной функции не возникает, потому что преобразования такого рода осуществляются автоматически. Мне на ум приходит всего несколько примеров, когда эта функция действительно нужна. Пусть, например, по какой-то причине вы поместили в каждый элемент <PLANET>в planets.xmlпо три элемента <NAME>и хотите использовать в качестве фактического названия планеты только первый элемент <NAME>: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET> <NAME>Mercury</NAME> <NAME>Venus</NAME> <NAME>Earth</NAME> <MASS UNITS="(Earth = 1)">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> <PLANET> <NAME>Venus</NAME> <NAME>Earth</NAME> <NAME>Mercury</NAME> <NAME>Planet of Love.</NAME> <MASS UNITS="(Earth = 1)">.815</MASS> <DAY UNITS="days">116.75</DAY> <RADIUS UNITS="miles">3716</RADIUS> <DENSITY UNITS="(Earth = 1)">.943</DENSITY> <DISTANCE UNITS="million miles">66.8</DISTANCE><!--В перигелии--> </PLANET> <PLANET> <NAME>Earth</NAME> <NAME>Mercury</NAME> <NAME>Venus</NAME> <NAME>The planet you're standing on.</NAME> <MASS UNITS="(Earth = 1)">1</MASS> <DAY UNITS="days">1</DAY> <RADIUS UNITS="miles">2107</RADIUS> <DENSITY UNITS="(Earth = 1)">1</DENSITY> <DISTANCE UNITS="million miles">128.4</DISTANCE><!--В перигелии--> </PLANET> </PLANETS> Предположим теперь, что вам нужно выбрать определенную планету, например, Венеру (Venus). Такая проверка не пройдет, потому что NAMEвернет набор узлов из всех дочерних элементов <NAME>контекстного узла, а так как у каждой планеты есть элемент <NAME>со значением «Venus», такое условие всегда будет истиной: <xsl:template match="PLANET"> <xsl:if test="NAME='Venus'"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:if> </xsl:template> Для того чтобы проверить первый элемент <NAME>в каждом элементе <PLANET>, можно воспользоваться функцией string, поскольку она возвращает не набор узлов, а строку: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:if test="string(NAME)='Venus'"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:if> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> . . . <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> Конечно, если нужно только выбрать первый дочерний элемент <NAME>контекстного узла, проще воспользоваться NAME[1]. string-length()Как можно предположить, функция string-lengthвозвращает длину (length) переданной ей строки. Функция применяется таким образом: number string-length(string?) В следующем примере я определяю длину названия каждой планеты при помощи string-length: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> Length of Planet Names </TITLE> </HEAD> <BODY> <H1> Length of Planet Names </H1> <xsl:apply-templates/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> is <xsl:value-of select-"string-length(NAME)"/> characters long. <BR/> </xsl:template> <xsl:template match="*"> </xsl:template> </xsl:stylesheet> А вот результат: <HTML> <HEAD> <TITLE> Length of Planet Names </TITLE> </HEAD> <BODY> <H1> Length of Planet Names </H1> Mercury is 7 characters long. <BR> Venus is 5 characters long. <BR> Earth is 5 characters long. <BR> </BODY> </HTML> Этот результирующий документ показан на рис. 8.2. Рис. 8.2. Определение длины строк в XSLT substring()Функция substringвозвращает подстроку переданной ей строки. Функция применяется следующим образом: string substring(source-string, start-position, number-of-characters?) Функция принимает параметры source-string(исходная строка), start-position(начальная позиция) и необязательный параметр number-of-characters(количество символов). Функция возвращает подстроку исходной строки с начальной позиции и до указанного количества символов или до конца строки, если это количество не задано. Функция substring— одна из трех функций, оперирующих с подстроками: substring-before, возвращающая строку перед найденной подстрокой, сама substring, возвращающая заданную подстроку, и substring-after, возвращающая строку после найденной подстроки. В следующем примере (листинг 8.9) задействованы все три функции: в этом случае я разбил название планеты Меркурий (Mercury) на три подстроки — «Mer», «c» и «ury» — и затем объединил их снова. Вот как это можно сделать при помощи трех рассматриваемых функций (дополнительная информация о substring-beforeи substring-afterприведена в следующих двух разделах). Листинг 8.9. Применение функций substring-before, substring и substring-after <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> Planetary Information </TITLE> </HEAD> <BODY> <H1> Planetary Information </H1> <xsl:apply-templates/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:if test="NAME='Mercury'"> The first planet is
substring(NAME, 4, 1), substring-after(NAME, 'c'))"/>. <BR/> </xsl:if> </xsl:template> <xsl:template match="*"> </xsl:template> </xsl:stylesheet> Вот результирующий документ, созданный этим преобразованием: <HTML> <HEAD> <TITLE> Planetary Information </TITLE> </HEAD> <BODY> <H1> Planetary Information </H1> The first planet is Mercury. <BR> </BODY> </HTML> substring-after()Функция substring-afterвозвращает подстроку, которая следует за найденной строкой. В эту функцию передается строка, подстрока, которую нужно найти внутри строки, — и функция возвращает подстроку, расположенную после совпадающей части строки, если она была найдена; иначе функция возвращает пустую строку. Функция применяется следующим образом: string substring-after(string, string-to-match) substring-before()В функцию substring-beforeпередается строка, подстрока, которую нужно найти внутри строки, и функция возвращает подстроку, расположенную перед совпадающей частью строки, если она была найдена; иначе функция возвращает пустую строку. Функция применяется следующим образом: string substring-before(string, string-to-match) В разделе substring()приведен пример применения функций substring-before, substringи substring-after. translate()Функция translateслужит для перевода или замены определенных символов. (Эта функция во многом похожа на операцию tr в Perl, если вы знакомы с операциями.) В функцию передаются две строки: одна задает список символов поиска, вторая задает список символов, которыми нужно заменить найденные символы. Функция применяется следующим образом: string translate(string, from-characters, to-characters) Например, если третий символ из from-charactersбудет найден в string, то третий символ из to-charactersзаменит его в результирующей строке. В следующем примере такая функция translate translate("steve-starpowder.com", "-", "@") возвратит результирующую строку «steve@starpowder.com». В этом примере я просто привожу строку к нижнему регистру: translate("XSLT", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz") Функции XPath для работы с числамиВ XPath имеются следующие функции для работы с числами: • ceiling(). Возвращает наименьшее целое, большее переданного функции числа, • floor(). Возвращает наибольшее целое, меньшее переданного функции числа; • number(). Приводит переданный ей параметр к числу; • round(). Округляет переданное функции число до ближайшего целого; • sum(). Возвращает сумму переданных функции чисел. В следующих разделах мы рассмотрим примеры применения каждой из этих функций. ceiling()Функция ceilingвозвращает наименьшее целое, которое все еще больше переданного функции числа, то есть она возвращает следующее большее целое. Функция применяется следующим образом: number ceiling(number) Например, выражение ceiling(3.1415926535) возвращает 4. floor()Функция floor— парная к функции ceiling. Она возвращает наибольшее целое, которое все еще меньше переданного ей числа. Другими словами, функция возвращает предыдущее целое. Функция применяется следующим образом: number floor(number) В этом примере я при помощи floorпреобразую длительность дней планет в целые числа: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="floor(.)"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> А вот результирующий документ: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58 days</TD> </TR> <TR> <TD>Venus</TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> <TD>116 days</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> number()Функция numberприводит свой аргумент к числу. Функция применяется следующим образом: number number(object?) В этом примере я передаю numberстроку: number("456.7") Это выражение возвращает число 456.7. Если вы попытаетесь преобразовать что-то, что numberне сможет перевести в число, функция вернет значение XPath NaN(Not a Number, не число). NaN— корректное значение, которое можно проверять в выражениях Xpath. round()Функция roundокругляет переданное ей значение. Функция применяется следующим образом: number round(number) Например, round(3.1415926535)возвращает 3, round(4.5)возвращает 5 и round(-1.5)возвращает -1. sum()Функция sumсуммирует численные значения набора узлов и возвращает результат. Функция применяется следующим образом: number sum(node-set) Взгляните на следующий пример из главы 4, который находит средние массы планет в planets.xml: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <HTML> <BODY> The average planetary mass is <xsl:value-of select="sum(child::PLANET/child::MASS) div count(child::PLANET/child::MASS)"/> </BODY> </HTML> </xsl:template> </xsl:stylesheet> Логические функции XPathXPath также поддерживает следующий набор логических функций: • boolean(). Приводит аргумент к логическому значению; • false(). Возвращает false (ложь); • lang(). Проверяет, совпадает ли язык, установленный в атрибуте xml:lang, с языком, переданным в функцию; • not(). Инвертирует значение true\false аргумента; • true(). Возвращает true (истина). boolean()Функция booleanпреобразует аргумент в логическое значение. Функция применяется следующим образом: boolean boolean(object) Ниже показано, как функция работает с различными типами XPath: • number. Если число равно нулю, результатом будет ложь; иначе результат — истина. NaN всегда возвращает ложь; • string. Если строка не пуста, результат — истина, иначе — ложь; • boolean. Значение не изменяется; • nodeset. Пустой набор узлов дает ложь, не пустой — истину; • Фрагмент результирующего дерева в XSLT 1.0 всегда дает истину. false()Функция falseвозвращает ложь. Она применяется следующим образом: boolean false() В XPath не определены логические константы, поэтому для того чтобы присвоить переменной значение false, нужно прибегнуть к функции false. (С переменными вы познакомитесь в главе 9.) lang()Функция langпроверяет, совпадает ли язык контекстного узла (определенный в атрибуте xml:lang) с переданным ей языком. Функция применяется следующим образом: boolean lang(string) Функции нужно передать строку, определяющую язык в спецификации XML, как, например, «en» для английского, «de» для немецкого и «jp» для японского. В следующем примере я проверяю, написан ли исходный документ на английском. Сначала я устанавливаю атрибут xml:langв planets.xmlв значение, соответствующее английскому языку: <?xml version="1.0"?> <?xml=stylesheet type="text/xml" href="planets.xsl"?> <PLANETS xml:lang="en"> <PLANET> <NAME>Mercury</NAME> <MASS UNITS="(Earth = 1)">.0553</MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии--> </PLANET> . . . Теперь я удостоверяюсь, что исходный документ на самом деле написан на английском, перед тем как применять к нему шаблоны: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <xsl:if test="lang('en')"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:if> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> </xsl:stylesheet> not()Функция notинвертирует логическое значение аргумента. При передаче аргумента с логическим значением true она возвращает false; при передаче аргумента со значением false функция возвращает true. Функция применяется следующим образом: boolean not(boolean) В следующем примере, который вы уже видели в главе 4, я хочу выбрать только те элементы, у которых есть оба атрибута — COLORи POPULATED. Для этого я применяю предикат выбора « [@COLOR and (@POPULATED]». Для того, чтобы убрать другие элементы (чтобы правила по умолчанию не включали их текст в результирующий документ), я использую предикат « [not(@COLOR) or not(@POPULATED)]»: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET[@COLOR and @POPULATED]"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="PLANET[not(@COLOR) or not(@POPULATED)]"> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот результат: <HTML> <HEAD> <TITLE> Colorful, Populated Planets </TITLE> </HEAD> <BODY> <H1> Colorful, Populated Planets </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> true()Функция trueвозвращает значение true. Она применяется следующим образом: boolean true() В XPath не определены логические константы, поэтому для того чтобы присвоить переменной значение true, нужно прибегнуть к функции true. (С переменными вы познакомитесь в главе 9.) Элемент <xsl:decimal-format>: создание числовых форматовПеред тем как закончить обсуждение функций XSLT и XPath, я хочу рассмотреть специальный элемент XSLT, <xsl:decimal-format>, единственное предназначение которого заключается в работе только с одной функцией format-number. В частности, при помощи этого элемента определяются символы и идентификаторы, которые будет использовать format-number. У этого элемента несколько атрибутов: • name(необязательный). Имя десятичного формата. Принимает значение типа QName. Если формат не задан, используется десятичный формат по умолчанию; • decimal-separator(необязательный). Определяет символ, разделяющий целую и дробную части числа. По умолчанию «.». Устанавливается в символ; • grouping-separator(необязательный). Определяет символ, разделяющий группы разрядов. По умолчанию «,».Устанавливается в символ; • infinity(необязательный). Задает строку, обозначающую положительную бесконечность. По умолчанию «Infinity» (бесконечность). Устанавливается в строку; • minus-sign(необязательный). Определяет символ, представляющий знак минуса. По умолчанию «-». Устанавливается в символ; • NaN(необязательный). Определяет строку, представляющую значение «Not a Number» (не число). По умолчанию «NaN». Устанавливается в строку; • percent(необязательный). Определяет символ, представляющий знак процента. По умолчанию «%». Устанавливается в символ; • per-mille(необязательный). Определяет символ, представляющий знак промилле, то есть тысячных долей. По умолчанию «‰». Устанавливается в символ; • zero-digit(необязательный). Определяет символ для использования в форматирующих строках для указания лидирующего или завершающего нуля. По умолчанию «0». Устанавливается в символ; • digit(необязательный). Определяет символ для использования в форматирующих строках для указания цифры. По умолчанию «0». Устанавливается в символ; • pattern-separator(необязательный). Определяет символ, разделяющий образцы для положительных и отрицательных чисел. По умолчанию «;». Устанавливается в символ. Этот элемент — верхнего уровня, и он всегда пуст. При помощи этого элемента можно устанавливать форматирующие символы для функции format-number. Элемент xsl:decimal-format, у которого не установлен атрибутname, становится десятичным форматом по умолчанию. Наличие более одного элемента xsl:decimal-formatпо умолчанию или нескольких элементов xsl:decimal-formatс одним и тем же именем является ошибкой. В следующем примере я форматирую числа в planets.xmlпри помощи европейского формата числа, а именно — я использую запятую вместо десятичной точки для разделения целой и дробной частей, и точку вместо запятой для разделения групп разрядов. Для этого мне нужно только задать новое форматирование при помощи <xsl:decimal-format>и затем применить его в format-number: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:decimal-format decimal-separator="," grouping-separator="."/> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Formatted Planets Table </TITLE> </HEAD> <BODY> <H1> The Formatted Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="format-number(., '#.##')"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="format-number(., '#.##')"/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="format-number(., '###.#')"/> <xsl text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот результирующий документ: <HTML> <HEAD> <TITLE> The Formatted Planets Table </TITLE> </HEAD> <BODY> <H1> The Formatted Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>0,055 (Earth = 1)</TD> <TD>1,516 miles</TD> <TD>58,65 days</TD> </TR> <TR> <TD>Venus</TD> <TD>0,815 (Earth = 1)</TD> <TD>3,716 miles</TD> <TD>116,75 days</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2,107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> Этот документ показан на рис. 8.3. Рис. 8.3. Задание десятичных форматов Вот и все — теперь вы можете задавать параметры форматирования для функции format-number. Новые функции в XSLT 2.0 и XPath 2.0В XSLT 2.0 и XPath 2.0 планируется включить весьма много функций. В частности, для XSLT 2.0 внесены в план следующие положения: • должны быть включены функции, которые могут работать с QName, для того, чтобы можно было применить объявления пространств имен из области видимости; • должны быть добавлены функции форматирования даты для поддержки схемы XML; • должна быть включена функция для преобразования относительных идентификаторов URI в абсолютные; • должны быть включены функции, упрощающие группировку. В XSLT 2.0 должно быть возможно группировать узлы на основе их строковых значений. В XPath 2.0 в отношении функций планируются следующие изменения: • должен быть расширен набор агрегирующих функций — например, включены функции минимума и максимума; • для наборов узлов должны быть реализованы функции пересечения и разности; • должна быть включена поддержка дополнительных строковых функций, таких как функции для замены строк, заполнения символами и преобразований регистров в строках; • должны быть включены агрегирующие функции для работы с коллекциями такими как наборы узлов; • должны быть определены функции для работы со стандартными типами схемы XML. На этом мы заканчиваем рассмотрение функций XSLT и XPath, применяемых при преобразованиях. В главе 9 мы изучим переменные, параметры и другие специальные темы. |
|
|||||||||||||||||||||||||||||||||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх |
|||||||||||||||||||||||||||||||||||
|