Главная » XSLT » Реализация на XSLT сценариев, приведенных в спецификации W3C XML Query

0

Задача

Требуется выполнить запрос, аналогичный одному из приведенных в качестве при­меров в документе http://www.w3.org/TR/2001/WD-xmlquery-use-cases-20011220, но вместо языка XQuery (http://www.w3.org/TR/xquery/) использовать XSLT.

Решение

В следующих примерах приведены решения на XSLT для большинства сцена­риев, приведенных в документе консорциума W3C. Описания сценариев почти дословно взяты из этого документа.

1. Сценарий «XMP»: описание и образцы решения.

В этом сценарии приведено несколько примеров запросов, иллюстрирую­щих требования, сформулированные сообществами разработчиков баз данных и средств обработки документов. Данные для запросов представле­ны в примерах 9.10 – 9.13.

Пример 9.10. bib.xml

<bib>

<book year="19 94">

<title>TCP/IP Illustrated</title>

<author><last>Stevens</last><first>W.</first></author> <publisher>Addison-Wesley</publisher> <price> 65.95</price> </book>

<book year="19 92">

<title>Advanced Programming in the Unix environment</title> <author><last>Stevens</last><first>W.</first></author> <publisher>Addison-Wesley</publisher> <price>65.95</price> </book>

<book year="2000">

<title>Data on the Web</title>

<author><last>Abiteboul</last><first>Serge</first></author> <author><last>Buneman</last><first>Peter</first></author> <author><last>Suciu</last><first>Dan</first></author> <publisher>Morgan Kaufmann Publishers</publisher> <price> 39.95</price> </book>

<book year="19 9 9">

<title>The Economics of Technology and Content for Digital TV</title> <editor>

<last>Gerbarg</last><first>Darcy</first>

<affiliation>CITI</affiliation> </editor>

<publisher>Kluwer Academic Publishers</publisher> <price>12 9.95</price> </book>

</bib>

Пример 9.11. reviews.xml

<reviews> <entry>

<title>Data on the Web</title>

<price>34.95</price>

<review>

A very good discussion of semi-structured database

systems and XML. </review> </entry> <entry>

<title>Advanced Programming in the Unix environment</title>

<price>65.95</price>

<review>

A clear and detailed discussion of UNIX programming. </review> </entry> <entry>

<title>TCP/IP Illustrated</title>

<price>65.95</price>

<review>

One of the best books on TCP/IP. </review> </entry>

</reviews>

Пример 9.12. booksxml

<chapter>

<title>Data Model</title> <section>

<title>Syntax For Data Model</title> </section> <section>

<title>XML</title> <section>

<title>Basic Syntax</title> </section> <section>

<title>XML and Semistructured Data</title> </section> </section> </chapter>

Пример 9.13. prices.xml

<prices> <book>

<title>Advanced Programming in the Unix environment</title> <source>www.amazon.com</source> <price>65.95</price> </book> <book>

<title>Advanced Programming in the Unix environment </title> <source>www.bn.com</source> <price>65.95</price> </book> <book>

<title> TCP/IP Illustrated </title> <source>www.amazon.com</source> <price>65.95</price> </book> <book>

<title> TCP/IP Illustrated </title> <source>www.bn.com</source> <price>65.95</price> </book> <book>

<title>Data on the Web</title>

<source>www.amazon.com</source>

<price>34.95</price>

</book> <book>

<title>Data on the Web</title> <source>www.bn.com</source> <price>3 9.95</price> </book> </prices>

Вопрос 1. Перечислить книги в файле bib.xml, опубликованные издательством Addison-Wesley после 1991 года, указав для каждой год издания и название:

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="copy.xslt"/>

<xsl:template match="book[publisher = ‘Addison-Wesley’ and @year > 1991]">

<xsl:copy-of select="."/> </xsl:template>

<xsl:template match="book"/>

</xsl:stylesheet>

Вопрос 2. Создать плоский список всех пар название-автор из файла bib.xml, поместив каждую пару внутрь элемента result:

<xsl:template match="/"> <results>

<xsl:apply-templates select="bib/book/author"/> </results> </xsl:template>

<xsl:template match="author"> <result>

<xsl:copy-of select="preceding-sibling::title"/> <xsl:copy-of select="."/> </result> </xsl:template>

Вопрос 3. Для каждой книги в файле bib.xml вывести ее название и авторов, поместив их внутрь элемента result:

<xsl:template match="bib"> <results>

<xsl:for-each select="book"> <result>

<xsl:copy-of select="title"/> <xsl:copy-of select="author"/> </result> </xsl:for-each>

</results> </xsl:template>

Вопрос 4. Для всех авторов в файле bib.xml вывести имя автора и названия написанных им книг, поместив все это внутрь элемента result:

<xsl:template match="/"> <results>

<xsl:for-each select="//author[not(.=preceding::author)]"> <result>

<xsl:copy-of select="."/>

<xsl:for-each select="/bib/book[author=current()]">

<xsl:copy-of select="title"/> </xsl:for-each> </result> </xsl:for-each> </results>

Вопрос 5. Для каждой книги, найденной одновременно на сайтах http://www/ bn.com (bib.xml) и http://www.amazon.com (reviews.xml), вывести название и цену, указанную в каждом источнике:

<xsl:variable name="bn" select="document(‘bib.xml’)"/> <xsl:variable name="amazon" select="document(‘reviews.xml’)"/>

<!– Решение 1 –> <xsl:template match="/"> <books-with-prices>

<xsl:for-each select="$bn//book[title = $amazon//entry/title]"> <book-with-prices>

<xsl:copy-of select="title"/> <price-amazon><xsl:value-of

select="$amazon//entry [title=current()/title]/price"/></price-amazon> <price-bn><xsl:value-of select="price"/></price-bn> </book-with-prices> </xsl:for-each> </books-with-prices> </xsl:template>

<!– Решение 2 –> <xsl:template match="/"> <books-with-prices> <xsl:for-each select="$bn//book">

<xsl:variable name="bn-book" select="."/>

<xsl:for-each select="$amazon//entry[title=$bn-book/title]"> <book-with-prices>

<xsl:copy-of select="title"/>

<price-amazon><xsl:value-of select="price"/></price-amazon>

<price-bn><xsl:value-of select="$bn-book/price"/></price-bn> </book-with-prices> </xsl:for-each> </xsl:for-each> </books-with-prices> </xsl:template>

Вопрос 6. Для каждой книги, у которой есть хотя бы один автор, вывести название и имена первых двух авторов, а также пустой элемент "et-al", если у книги есть еще авторы:

<xsl:template match="bib"> <xsl:copy>

<xsl:for-each select="book[author]"> <xsl:copy>

<xsl:copy-of select="title"/>

<xsl:copy-of select="author[position() &lt; = 2]"/> <xsl:if test="author[3]"> <et-al/> </xsl:if> </xsl:copy> </xsl:for-each> </xsl:copy> </xsl:template>

Вопрос 7. Перечислить названия и года издания всех книг, опубликованных издательством Addison-Wesley после 1991 года, в алфавитном порядке:

<xsl:template match="bib"> <xsl:copy>

<xsl:for-each select="book[publisher = ‘Addison-Wesley’ and @year > 1991]"> <xsl:sort select="title"/> <xsl:copy>

<xsl:copy-of select="@year"/> <xsl:copy-of select="title"/> </xsl:copy> </xsl:for-each> </xsl:copy> </xsl:template>

Вопрос 8. В документе books.xml найти названия всех разделов (элементы section) и глав (элементы chapter), в которых встречается слово "XML", независимо от уровня вложенности:

<xsl:template match="/"> <results>

<xsl:copy-of select="(//chapter/title | //section/title)[contains(.,’XML’)]"/> </results>

</xsl:template>

Вопрос 9. В документе prices.xml найти минимальную цену каждой книги и вывести ее в виде элемента "minprice", в котором название книги представлено атрибутом title:

<xsl:include href="../math/math.min.xslt"/>

<xsl:template match="/"> <results>

<xsl:for-each select="//book/title[not(. = ./preceding::title)]"> <xsl:variable name="min-price"> <xsl:call-template name="math:min">

<xsl:with-param name="nodes" select="//book[title =

current()]/price"/>

</xsl:call-template> </xsl:variable> <minprice title="{.}">

<price><xsl:value-of select="$min-price"/></prices> </minprice> </xsl:for-each> </results> </xsl:template>

Вопрос 10. Для каждой книги, имеющей автора, вернуть ее название и всех авторов. Для каждой книги, имеющей редактора, вернуть элемент reference, содержащий название книги и название организации, к которой принадлежит редактор:

<xsl:template match="bib"> <xsl:copy>

<xsl:for-each select="book[author]"> <xsl:copy> <xsl:copy-of select="title"/> <xsl:copy-of select="author"/> </xsl:copy> </xsl:for-each>

<xsl:for-each select="book[editor]"> <reference> <xsl:copy-of select="title"/>

<org><xsl:value-of select="editor/affiliation"/></org> </reference> </xsl:for-each> </xsl:copy> </xsl:template>

Вопрос 11. Найти пары книг, у которых разные названия, но один и тот же набор авторов (возможно, перечисленных в другом порядке):

<xsl:include href="query.equal-values.xslt"/>

<xsl:template match="bib"> <xsl:copy> <xsl:for-each select="book[author]"> <xsl:variable name="book1" select="."/>

<xsl:for-each select="./following-sibling::book[author]"> <xsl:variable name="same-authors">

<xsl:call-template name="query:equal-values">

<xsl:with-param name="nodes1" select="$book1/author"/> <xsl:with-param name="nodes2" select="author"/> </xsl:call-template> </xsl:variable>

<xsl:if test="string($same-authors)"> <book-pair>

<xsl:copy-of select="$book1/title"/> <xsl:copy-of select="title"/> </book-pair> </xsl:if> </xsl:for-each> </xsl:for-each> </xsl:copy> </xsl:template>

2. Сценарий «TREE»: запросы, сохраняющие иерархию.

Некоторые XML-документы обладают весьма гибкой структурой, в которой текст перемежается элементами, причем многие элементы необязательны. Структура документов может быть очень разнообразной. Обычно порядок следования и вложенности элементов имеет значение. Язык запросов XML должен иметь возможность извлекать элементы из документов с сохране­нием иерархии. Настоящий сценарий иллюстрирует это требования на примере гибкого документа Book. В примерах 9.14 и 9.15 приведена DTD-схема и XML-данные, используемые в запросах.

Пример 9.14. book.dtd

<!ELEMENT book (title, author+, section+)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)>

<!ELEMENT section (title, (p | figure | section)* )> <!ATTLIST section

id                            ID      #IMPLIED

difficulty            CDATA #IMPLIED>

<!ELEMENT p (#PCDATA)>

:!ELEMENT figure (title, image)> !ATTLIST figure

width             CDATA #REQUIRED

height CDATA #REQUIRED > :!ELEMENT image EMPTY> !ATTLIST image

source           CDATA #REQUIRED >

Пример 9.15. books.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE book SYSTEM "book.dtd"> <book>

<title>Data on the Web</title> <author>Serge Abiteboul</author> <author>Peter Buneman</author> <author>Dan Suciu</author> <section id="intro" difficulty="easy" > <title>Introduction</title> <p>Text … </p> <section>

<title>Audience</title> <p>Text … </p> </section> <section>

<title>Web Data and the Two Cultures</title> <p>Text … </p>

<figure height="400" width="400">

<title>Traditional client/server architecture</title> <image source="csarch.gif"/> </figure> <p>Text … </p> </section> </section>

<section id="syntax" difficulty="medium" > <title>A Syntax For Data</title> <p>Text … </p>

<figure height="200" width="500">

<title>Graph representations of structures</title> <image source="graphs.gif"/> </figure> <p>Text … </p> <section>

<title>Base Types</title> <p>Text … </p>

</section> <section>

<title>Representing Relational Databases</title> <p>Text … </p>

figure height="250" width="400">

<title>Examples of Relations</title> <image source="relations.gif"/> </figure> </section> <section>

<title>Representing Object Databases</title> <p>Text … </p> </section> </section> </book>

Вопрос 1. Подготовить (иерархическое) оглавление книги Book1, перечислив все разделы вместе с названиями. Сохранить все атрибуты каждого элемента <section>, если таковые присутствуют:

<xsl:template match="book"> <toc>

<xsl:apply-templates/> </toc> </xsl:template>

<!– Копировать элемент toc –>

<xsl:template match="section | section/title | section/title/text()"> <xsl:copy>

<xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template>

<!– Подавить все прочие элементы –> <xsl:template match="* | text()"/>

Вопрос 2. Подготовить (плоский) список рисунков для книги Book1, перечислив все рисунки вместе с названием. Сохранить все атрибуты каждого элемента <figure>, если таковые присутствуют:

<xsl:template match="book"> <figlist>

<xsl:for-each select=".//figure"> <xsl:copy>

<xsl:copy-of select="@*"/> <xsl:copy-of select="title"/> </xsl:copy> </xsl:for-each>

</figlist> </xsl:template>

Вопрос 3. Сколько в книге Bookl разделов и сколько рисунков?

<xsl:template match="/">

<section-count><xsl:value-of select="count(//section)"/></section-count> <figure-count><xsl:value-of select="count(//figure)"/></figure-count> </xsl:template>

Вопрос 4. Сколько в книге Bookl разделов верхнего уровня?

<xsl:template match="book"> <top_section_count>

<xsl:value-of select="count(section)"/> </top_section_count> </xsl:template>

Вопрос 5. Создать плоский список элементов section в книге Bookl. Вместо исходных атрибутов у каждого элемента section должно быть два атрибута: название раздела и количество рисунков, содержащихся в данном разделе без учета подразделов:

<xsl:template match="book"> <section_list>

<xsl:for-each select=".//section">

<section title="{title}" figcount="{count(figure)}"/> </xsl:for-each> </section_list> </xsl:template>

Вопрос 6. Создать иерархический список элементов section в книге Bookl, сохранив исходные атрибуты и иерархию. Внутрь каждого элемента section поместить название раздела и элемент, содержащий количество рисунков в нем без учета подразделов. См. примеры 9.16 и 9.17:

Пример 9.16. Решение, вытекающее из моей интерпретации условия

<xsl:template match="book"> <toc>

<xsl:apply-templates select="section"/> </toc>

</xsl:template>

<xsl:template match="section"> <xsl:copy>

<xsl:copy-of select="@*"/> <xsl:copy-of select="title"/>

<figcount><xsl:value-of select="count(figure)"/></figcount> <xsl:apply-templates select="section"/> </xsl:copy> </xsl:template>

Пример 9.17. Что имел в виду W3C, исходя из приведенного результата и запроса на языке XQuery

<xsl:template match="book"> <toc>

<xsl:for-each select="//section">

<xsl:sort select="count(ancestor::section)"/> <xsl:apply-templates select="."/> </xsl:for-each> </toc>

</xsl:template>

<xsl:template match="section"> <xsl:copy>

<xsl:copy-of select="@*"/> <xsl:copy-of select="title"/>

<figcount><xsl:value-of select="count(figure)"/></figcount> <xsl:apply-templates select="section"/> </xsl:copy> </xsl:template>

3. Сценарий «SEQ»: запросы, основанные на последовательности.

В этом сценарии иллюстрируются запросы, основанные на последователь­ности, в которой элементы появляются в документе. Хотя в большинстве традиционных СУБД и объектных систем последовательность не играет роли, в структурированных документах она может иметь значение. Ниже представлены запросы к медицинскому отчету.

<!DOCTYPE report [

<!ELEMENT report (section*)>

<!ELEMENT section (section.title, section.content)> <!ELEMENT section.title (#PCDATA )>

<!ELEMENT section.content (#PCDATA | anesthesia | prep

| incision | action | observation )*> <!ELEMENT anesthesia (#PCDATA)> <!ELEMENT prep ( (#PCDATA | action)* )>

<!ELEMENT incision ( (#PCDATA | geography | instrument)* )> <!ELEMENT action ( (#PCDATA | instrument )* )> <!ELEMENT observation (#PCDATA)> <!ELEMENT geography (#PCDATA)> <!ELEMENT instrument (#PCDATA)>

]>

<report> <section>

<section.title>Пpoцедypa</section.title> <section.content>

Пациентку доставили в операционную, уложили на спину и провели

<anesthesia>BBOflHyra общую анестезию.</anesthesia>

<prep>

<action>flnn опорожнения мочевого пузыря в него ввели катетер Фоли, после чего кожу живота обработали антисептическим раствором и драпировали стерильной тканью. </prep>

<incision>Произвели дугообразный разрез

<geography> по средней линии живота ниже пyпка</geography> и рассекли подкожную клетчатку <instrument>электрокаyтером.</instrument> </incision> По обнаружении фасции

<action^ обеих сторон от средней линии был наложен шов-держалка

Максон #2 0.

</action>

<incision>

После рассечения фасции

<instrument>электрокаyтером</instrument> вскрыли брюшину. </incision>

<observation>По обнаружении тонкой ^m^</observation> <action>

под визуальным контролем ввели <instrument>троакар Xассона</instrument>. </action> <action>

<instrument>Tроакар</instrument>

имплантировали в фасцию при помощи шва-держалки. </action> </section.content> </section> </report>

Вопрос 1. Какие инструменты были использованы для второго разреза (incision) в разделе Procedure документа Reportl?

<xsl:template match="section[section.title = ‘Procedure’]">

<xsl:copy-of select="(.//incision)[2]/instrument"/> </xsl:template>

Вопрос 2. Исходя из раздела "Процедура" документа Reportl, какие первые два инструмента следует использовать?

<xsl:template match="section[section.title = ‘Процедура’]">

<xsl:copy-of select="(.//instrument)[position() &lt; = 2]"/> </xsl:template>

Вопрос 3. Какие инструменты были использованы в первых двух действиях после второго разреза?

<xsl:template match="report">

<!– i2 = второй элемент incision в отчете –> <xsl:variable name="i2" select="(.//incision)[2]"/> <!– Для всех действий (action), следующих за i2,

выбрать инструменты, использованные в первых двух –> <xsl:copy-of

select="($i2/following::action)[position() &lt;= 2]/instrument"/> </xsl:template>

Вопрос 4. Найти все разделы "Процедура", в которых первому элементу incision не предшествует ни один элемент anesthesia:

<xsl:template match="section[section.title = ‘Процедура’]"> <xsl:variable name="i1" select="(.//incision)[1]"/> <xsl:if test=".//anesthesia[preceding::incision = $i1]">

<xsl:copy-of select="current()"/> </xsl:if> </xsl:template>

Если результат этого запроса не пуст, то не за горами судебное разбира­тельство!

Вопрос 5. Что происходило между первым и вторым разрезами?

<xsl:template match="report"> <critical_sequence>

<!– i1 = первый элемент incision в отчете –>

<xsl:variable name="i1" select="(.//incision)[1]"/>

<!– i2 = второй элемент incision в отчете –>

<xsl:variable name="i2" select="(.//incision)[2]"/>

<!– скопировать все узлы-братья, следующие за i1, которым

не предшествует элемент i2 и которые сами не совпадают с i2 –>

<xsl:for-each select="$i1/following-sibling::node()

[not(./preceding::incision = $i2) and not(. = $i2)]"> <xsl:copy-of select="."/> </xsl:for-each> </critical_sequence> </xsl:template>

В вопросах 4 и 5 я предполагаю, что строковые значения всех элементов incision различны. Для данных из приведенного примера это справедливо, но в общем случае может быть и не так. Чтобы получить правильное реше­ние, воспользуйтесь рецептом 4.2. Так, в вопросе 4 проверка должна выг­лядеть так:

test=".//anesthesia[count(./preceding::incision | $i1) = count(./preceding::incision)]"

4. Сценарий «R»: доступ к реляционным данным.

Одно из важных применений языка запросов к XML – это доступ к дан­ным, хранящимся в реляционной базе. В настоящем сценарии описывается один из возможных способов реализации такого доступа. В реляционной СУБД может быть определено представление, в котором каждая таблица (отношение) принимает форму XML-документа. Например, документный элемент может представлять таблицу в целом, а каждая ее строка (кортеж) представляется вложенным элементом. В элементы, соответствующие строкам, в свою очередь вложены элементы, представляющие колонки. Колонкам, допускающим значения null, соответствуют необязательные элементы; если такой элемент опущен, значит, соответствующая ему ко­лонка содержит null.

Рассмотрим, например, реляционную базу для онлайнового аукциона. В ней есть таблица USERS, в которой хранится информация о зарегистрирован­ных пользователях, для каждого из которых определен уникальный иденти­фикатор. Пользователь может либо выставлять товар на аукцион, либо при­нимать участие в торгах. В таблице ITEMS хранятся торгуемые сейчас или недавно выставленные на торги товары, причем в каждой ее строке записан идентификатор пользователя, предложившего товар. В таблице BIDS хранит­ся информация о торгах: идентификатор торгующегося пользователя и иден­тификатор торгуемого товара.

Поскольку количество запросов в этом сценарии велико, мы реализуем только часть. Реализация остальных запросов будет неплохим упражнением для тех, кто хотел бы отточить свое владение XSLT. См. примеры 9.18 – 9.20.

Пример 9.18. users.xml

<users>

<user_tuple>

<userid>U01</userid> <name>Tom Jones</name> <rating>B</rating> </user_tuple> <user_tuple>

<userid>U0 2</userid> <name>Mary Doe</name> <rating>A</rating> </user_tuple> <user_tuple>

<userid>U0 3</userid> <name>Dee Linquent</name> <rating>D</rating> </user_tuple> <user_tuple>

<userid>U0 4</userid>

<name>Roger Smith</name> <rating>C</rating> </user_tuple> <user_tuple>

<userid>U0 5</userid> <name>Jack Sprat</name> <rating>B</rating> </user_tuple> <user_tuple>

<userid>U0 6</userid> <name>Rip Van Winkle</name> <rating>B</rating> </user_tuple> </users>

Пример 9.19. items.xml

<items>

<item_tuple>

<itemno>10 01</itemno>

<description>Red Bicycle</description> <offered_by>U01</offered_by> <start_date>9 9-01-0 5</start_date> <end_date>9 9-01-2 0</end_date> <reserve_price>4 0</reserve_price> </item_tuple> <item_tuple>

<itemno>10 02</itemno>

<description>Motorcycle</description> <offered_by>U02</offered_by> <start_date>9 9-0 2-11</start_date> <end_date>9 9-0 3-15</end_date> <reserve_price>5 0 0</reserve_price> </item_tuple> <item_tuple>

<itemno>1003</itemno>

<description>Old Bicycle</description> <offered_by>U02</offered_by> <start_date>9 9-01-10</start_date> <end_date>9 9-02-2 0</end_date> <reserve_price>2 5</reserve_price> </item_tuple> <item_tuple>

<itemno>1004</itemno>

<description>Tricycle</description> <offered_by>U01</offered_by> <start_date>9 9-0 2-2 5</start_date> <end_date>9 9-0 3-0 8</end_date> <reserve_price>15</reserve_price> </item_tuple> <item_tuple>

<itemno>10 05</itemno>

<description>Tennis Racket</description> <offered_by>U03</offered_by> <start_date>9 9-0 3-19</start_date> <end_date>9 9-0 4-30</end_date> <reserve_price>2 0</reserve_price> </item_tuple> <item_tuple>

<itemno>1006</itemno>

<description>Helicopter</description> <offered_by>U03</offered_by> <start_date>9 9-0 5-0 5</start_date> <end_date>9 9-05-25</end_date> <reserve_price>5 0 0 0 0</reserve_price> </item_tuple> <item_tuple>

<itemno>10 07</itemno>

<description>Racing Bicycle</description> <offered_by>U04</offered_by> <start_date>9 9-01-2 0</start_date> <end_date>9 9-02-2 0</end_date> <reserve_price>2 0 0</reserve_price> </item_tuple> <item_tuple>

<itemno>1008</itemno>

<description>Broken Bicycle</description> <offered_by>U01</offered_by> <start_date>9 9-0 2-0 5</start_date> <end_date>9 9-0 3-0 6</end_date> <reserve_price>2 5</reserve_price> </item_tuple> </items>

Пример 9.20. bids.xml

<bids>

<bid_tuple>

<userid>U02</userid> <itemno>10 01</itemno> <bid> 35</bid>

<bid_date>99-01-07 </bid_date> </bid_tuple> <bid_tuple>

<userid>U0 4</userid> <itemno>10 01</itemno> <bid>4 0</bid>

<bid_date>9 9-01-0 8</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 2</userid> <itemno>1001 </itemno> <bid>45</bid>

<bid_date>9 9-01-11</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 4</userid> <itemno>10 01</itemno> <bid>5 0</bid>

<bid_date>9 9-01-13</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 2</userid> <itemno>10 01</itemno> <bid>55</bid>

<bid_date>9 9-01-15</bid_date> </bid_tuple> <bid_tuple>

<userid>U01</userid> <itemno>10 02</itemno> <bid>4 0 0</bid>

<bid_date>9 9-02-14</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 2</userid> <itemno>10 02</itemno> <bid>60 0</bid>

<bid_date>9 9-02-16</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 3</userid> <itemno>10 02</itemno>

<bid>8 0 0</bid>

<bid_date>9 9-02-17</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 4</userid> <itemno>10 02</itemno> <bid>1000</bid> <bid_date>9 9-02-25</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 2</userid> <itemno>10 02</itemno> <bid>12 0 0</bid> <bid_date>9 9-0 3-02</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 4</userid>

<itemno>1003</itemno>

<bid>15</bid>

<bid_date>9 9-01-22</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 5</userid> <itemno>1003</itemno> <bid>2 0</bid>

<bid_date>9 9-02-0 3</bid_date> </bid_tuple> <bid_tuple>

<userid>U01</userid> <itemno>1004</itemno> <bid>4 0</bid>

<bid_date>9 9-0 3-05</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 3</userid> <itemno>10 07</itemno> <bid>17 5</bid>

<bid_date>9 9-01-25</bid_date> </bid_tuple> <bid_tuple>

<userid>U0 5</userid> <itemno>10 07</itemno> <bid>2 0 0</bid>

<bid date>99-02-08</bid date>

</bid_tuple> <bid_tuple>

<userid>U0 4</userid> <itemno>10 07</itemno> <bid>22 5</bid>

<bid_date>9 9-02-12</bid_date> </bid_tuple> </bids>

Вопрос 1. Перечислить идентификаторы и описания всех велосипедов (bicycle), по которым сейчас идут торги, упорядочив список по идентификатору:

<xsl:include href="../date/date.date-time.xslt"/>

<!– Чтобы результат был похож на приведенный в документе W3C –> <xsl:param name="today" select="’1999-01-21’"/>

<xsl:template match="items">

<xsl:variable name="today-abs">

<xsl:call-template name="date:date-to-absolute-day"> <xsl:with-param name="date" select="$today"/> </xsl:call-template> </xsl:variable>

<result>

<xsl:for-each select="item_tuple">

<xsl:sort select="itemno" data-type="number"/>

<xsl:variable name="start-abs">

<xsl:call-template name="date:date-to-absolute-day">

<xsl:with-param name="date" select="start_date"/> </xsl:call-template> </xsl:variable>

<xsl:variable name="end-abs">

<xsl:call-template name="date:date-to-absolute-day"> <xsl:with-param name="date" select="end_date"/> </xsl:call-template> </xsl:variable>

<xsl:if test="$start-abs &lt; = $today-abs and $end-abs >= $today-abs and contains(description, ‘Bicycle’)"> <xsl:copy> <xsl:copy-of select="itemno"/> <xsl:copy-of select="description"/>

</xsl:copy> </xsl:if>

</xsl:for-each> </result> </xsl:template>

Вопрос 2. Для всех велосипедов вывести идентификатор товара, его описание и наивысшее предложение (если есть хотя бы одно), упорядочив список по идентификатору:

<xsl:include href="../math/math.max.xslt"/>

<xsl:template match="items">

<result>

<xsl:for-each select="item_tuple[contains(description,’Bicycle’)]"> <xsl:sort select="itemno" data-type="number"/>

<xsl:variable name="bids" select="document(‘bids.xml’)//bid_tuple[itemno=current()/itemno]/bid"/>

<xsl:variable name="high-bid"> <xsl:call-template name="math:max">

<xsl:with-param name="nodes" select="$bids"/> </xsl:call-template> </xsl:variable>

<xsl:copy> <xsl:copy-of select="itemno"/> <xsl:copy-of select="description"/> <high_bid><xsl:if test="$bids"><xsl:value-of

select="$high-bid"/></xsl:if></high_bid> </xsl:copy>

</xsl:for-each> </result> </xsl:template>

Вопрос 3. Найти все случаи, когда пользователь с рейтингом ниже (то есть больше в алфавитном порядке) "C" предлагает товар с зарезервированной ценой более 1000:

<!– Строго говоря, необязательно, но в условии не определена система рейтингования, поэтому выводим ее динамически! –> <xsl:variable name="ratings">

<xsl:for-each select="document(‘users.xml’)//user_tuple/rating"> <xsl:sort select="." data-type="text"/>

<xsl:if test="not(. = ./preceding::rating)">

<xsl:value-of select="."/> </xsl:if> </xsl:for-each> </xsl:variable>

<xsl:template match="items"> <result>

<xsl:for-each select="item_tuple[reserve_price > 1000]">

<xsl:variable name="user" select="document(‘users.xml’)//user_tuple[userid = current()/offered_by]"/>

<xsl:if test="string-length(substring-before($ratings,$user/rating)) > string-length(substring-before($ratings,’C’))"> <warning>

<xsl:copy-of select="$user/name"/> <xsl:copy-of select="$user/rating"/> <xsl:copy-of select="description"/> <xsl:copy-of select="reserve_price"/> </warning> </xsl:if> </xsl:for-each> </result> </xsl:template>

Вопрос 4. Вывести идентификаторы и описания всех товаров, по которым нет торгов:

<xsl:template match="items"> <result>

<xsl:for-each select="item_tuple">

<xsl:if test="not(document(‘bids.xml’)//bid_tuple[itemno = current()/itemno])"> <no_bid_item>

<xsl:copy-of select="itemno"/> <xsl:copy-of select="description"/> </no bid item>

</xsl:for-each> </result> </xsl:template>

5. Сценарий «SGML»: язык Standard Generalized Markup Language.

Документ и запросы для этого сценария были подготовлены для конфе­ренции 1992 года по языку Standard Generalized Markup Language (SGML).

Удобства ради определение типа документа (DTD-схема) и сам пример документа переведены с SGML на XML.

В настоящей главе эти запросы не приводятся, поскольку они мало чем от­личаются от рассмотренных в других сценариях. 6. Сценарий «TEXT»: полнотекстовый поиск.

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

При поиске компании по названию учитываются отдельные слова. Эти слова могут встречаться в названии в любом регистре и разделяться любы­ми символами пропуска.

Все запросы можно выразить на XSLT 1.0. Однако при этом потребуются достаточно сложные механизмы текстового поиска. Например, для боль­шинства сложных запросов нужно уметь проверять, входит ли в строку любое слово из заданного набора. Кроме того, для многих запросов необхо­димо учитывать разбиение текста на смысловые единицы, например, пред­ложения.

Принимая во внимание приемы, описанные в главе 1, должно быть понят­но, что решать такие задачи на XSLT возможно. Однако, придется запастись достаточно общей библиотекой утилит для поиска по тексту. Разработке по­добных библиотек посвящена глава 14, в которой мы еще вернемся к некото­рым наиболее сложным запросам для полнотекстового поиска. А пока ре­шим две самых простых задачи из сформулированных в документе W3C.

Вопрос 1. Найти все новости, в заголовке которые встречается название "Foobar Corporation":

<xsl:template match="news"> <result>

<xsl:copy-of select="news_item/title[contains(., ‘Foobar Corporation’)]"/> </result> </xsl:template>

Вопрос 2. Для каждой новости, относящейся к компании Gorilla Corporation, создать элемент "item_summary", содержащие краткое резюме: название, дату и первый абзац новости, разделенные точками. Новость считается релевантной, если название компании упоминается в любом месте содержимого новости: <xsl:template match="news"> <result>

<xsl:for-each select="news_item[contains(content,’Gorilla Corporation’)]"> <item_summary>

<xsl:value-of select="normalize-space(title)"/>. <xsl:text/>

<xsl:value-of select="normalize-space(date)"/>. <xsl:text/> <xsl:value-of select="normalize-space(content/par[1])"/> </item_summary> </xsl:for-each> </result> </xsl:template>

7. Сценарий «PARTS»: рекурсивная деталировка.

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

В качестве примера взята база данных, содержащая информацию о том, как одни детали собираются из других.

На вход подается «плоский» документ, в котором каждая деталь представле­на элементом <part>, имеющим атрибуты partid и name. Каждая деталь может входить или в более крупную деталь, в таком случае идентификатор (partid) более крупной детали задается в атрибуте partof. Рассматривае­мый документ можно было бы получить из базы данных, в которой каждая деталь представлена строкой таблицы с первичным ключом partid и вне­шним ключом partof, ссылающимся на partid.

Сложность заключается в том, чтобы написать запрос, который преобразу­ет «плоское» представление деталировки, основанное на внешних ключах, в иерархическое, где информация о составе сборок представлена самой структурой документа.

Входные данные описываются следующей DTD-схемой:

<!DOCTYPE partlist [

<!ELEMENT partlist (part*)> <!ELEMENT part EMPTY> <!ATTLIST part

partid CDATA #REQUIRED partof CDATA #IMPLIED name CDATA #REQUIRED>

]>

Хотя атрибуты partid и partof могли бы иметь тип ID и IDREF соот­ветственно, в приведенной выше схеме мы трактуем их как символьные данные, которые, возможно, были материализованы непосредственно из реля­ционной базы. Каждый атрибут partof соответствует в точности одному partid. Детали, не имеющие атрибута partof, не входят ни в какую сборку. Выходные данные описываются следующей DTD-схемой:

<!DOCTYPE parttree [

<!ELEMENT parttree (part*)> <!ELEMENT part (part*)> <!ATTLIST part

partid CDATA #REQUIRED name CDATA #REQUIRED>

]>

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

<?xml version="1.0" encoding="ISO-8 85 9-1"?> <partlist>

<part partid="0" name="car"/> <part partid="1" partof="0" name="engine"/> <part partid="2" partof="0" name="door"/> <part partid="3" partof="1" name="piston"/> <part partid="4" partof="2" name="window"/> <part partid="5" partof="2" name="lock"/> <part partid="10" name="skateboard"/> <part partid="11" partof="10" name="board"/> <part partid="12" partof="10" name="wheel"/> <part partid="20" name="canoe"/> </partlist>

Вопрос 1. Преобразовать документ из формата "partlist" в формат "parttree" (см. описание DTD выше). В результирующем документе вхождение одной детали в другую представлено вложенностью соответствующих элемен­тов <part>. Детали, не являющиеся частью более крупных деталей, должны быть представлены отдельными элементами верхнего уровня:

<xsl:template match="partlist"> <parttree>

<!– Начать с деталей, не входящих в более ^упные c6op^ –> <xsl:apply-templates select="part[not(@partof)]"/> </parttree> </xsl:template>

<xsl:template match="part">

<part partid="{@partid}" name="{@name}">

<xsl:apply-templates select="../part[@partof = current( )/@partid]"/> </part> </xsl:template>

Как выясняется, такие пpеoбpaзoвaния на XSLT выглядят даже пpoще, чем на XQuery. Вот, нaпpимеp, pешение той же задачи на языке XQuery, пpиведен- ное в документе W3C:

define function one_level (element $p) returns element {

<part partid="{ $p/@partid }" name="{ $p/@name }" >

{

for $s in document("partlist.xml")//part

where $s/@partof = $p/@partid return one_level($s)

}

</part>

}

<parttree> {

for $p in document("partlist.xml")//part[empty(@partof)] return one_level($p)

}

</parttree>

Даже не зная языка XQuery, легко видеть, что на нем рекурсию приходится реализовывать явно, тогда как в XSLT конструкция apply-templates позволяет записать решение более декларативно. Согласен, разница не так уж велика, но все же мне кажется, что для таких задач решение на XSLT элегантнее.

1. Сценарий «REF»: запросы, основанные на ссылках1.

Ссылки – важный аспект XML. В этом сценарии описывается база данных, в которой ссылки играют заметную роль, и приводится несколько репре­зентативных запросов, где эти ссылки используются. Предположим, что в файле census.xml имеются элементы для всех людей, участвовавших в последней переписи. Для каждого элемента person в ат­рибутах записаны имя (name), профессия (job) и ссылка на супруга/суп­ругу (spouse), если таковой имеется. spouse – это атрибут типа IDREF, соответствующий атрибуту name типа ID в элементе, описывающем суп­руга (супругу).

Отношение родитель-ребенок кодируется вложенностью элементов person. Иначе говоря, элемент, представляющий ребенка, содержится внутри элемента, представляющего его отца или мать. Из-за возможности смерти, развода или повторного брака ребенок может быть ассоциирован либо с отцом, либо с матерью (но не с тем и другим одновременно). В данном упражнении фраза «дети X» подразумевает также «детей супруга(и) X». На­пример, если Джо и Марта – супруги, при этом элемент Джо содержит эле­мент Сэм, а элемент Марта содержит элемент Дэйв, то детьми Джо и Марты считаются как Сэм, так и Дэйв. У каждого человека, участвовавшего в пере­писи, может быть 0, 1 или 2 родителя.

В этом сценарии мы будем использовать входной документ census.xml, описы­ваемый следующей DTD-схемой:

<!DOCTYPE census [

<!ELEMENT census (person*)> <!ELEMENT person (person*)> <!ATTLIST person

name ID               #REQUIRED

spouse IDREF #IMPLIED job     CDATA #IMPLIED >

]>

В следующем фрагменте описываются две семьи, члены которых связаны не­сколькими перекрестными браками:

<census>

<person name="Bill" job="Teacher"> <person name="Joe" job="Painter" spouse="Martha"> <person name="Sam" job="Nurse">

<person name="Fred" job="Senator" spouse="Jane"> </person> </person>

<person name="Karen" job="Doctor" spouse="Steve"> </person> </person>

<person name="Mary" job="Pilot">

<person name="Susan" job="Pilot" spouse="Dave">

</person> </person> </person>

<person name="Frank" job="Writer"> <person name="Martha" job="Programmer" spouse="Joe"> <person name="Dave" job="Athlete" spouse="Susan"> </person> </person>

<person name="John" job="Artist"> <person name="Helen" job="Athlete"> </person>

<person name="Steve" job="Accountant" spouse="Karen"> <person name="Jane" job="Doctor" spouse="Fred"> </person> </person> </person> </person> </census>

Вопрос 1. Найти супруга Марты:

<xsl:strip-space elements="*"/>

<xsl:template match="person[@spouse=’Martha’]"> <xsl:copy>

<xsl:copy-of select="@*"/> </xsl:copy>

</xsl:template>

Вопрос 2. Найти детей атлетов:

<xsl:template match="census">

<xsl:variable name="everyone" select="//person"/> <result>

<!– Для каждого человека, имеющего детей –> <xsl:for-each select="$everyone[person]"> <xsl:variable name="spouse"

select="$everyone[@spouse=current( )/@name]"/> <xsl:if test="./person/@job = ‘Athlete’ or

$spouse/person/@job = ‘Athlete’"> <xsl:copy>

<xsl:copy-of select="@*"/> </xsl:copy> </xsl:if> </xsl:for-each> </result> </xsl:template>

Вопрос 3. Найти всех людей, имеющих ту же профессию, что у одного из родителей:

Оставляю для самостоятельного упражнения.

Вопрос 4. Вывести имена родителей и детей, имеющих одну и ту же профес­сию, и включить в отчет саму профессию:

<xsl:template match="census">

<xsl:variable name="everyone" select="//person"/> <result>

<!– Для каждого человека, имеющего детей –> <xsl:for-each select="$everyone[person]">

<xsl:variable name="spouse"

select="$everyone[@spouse=current( )/@name]"/>

<xsl:apply-templates select="person[@job = current( )/@job]">

<xsl:with-param name="parent" select="@name"/> </xsl:apply-templates>

<xsl:apply-templates select="person[@job = $spouse/@job]">

<xsl:with-param name="parent" select="$spouse/@name"/> </xsl:apply-templates>

</xsl:for-each> </result> </xsl:template>

<xsl:template match="person"> <xsl:param name="parent"/>

<match parent="{$parent}" child="{@name}" job="{@job}"/> </xsl:template>

Вопрос 5. Вывести пары имен прадедушка (прабабушка)-правнук (правнучка):

<xsl:template match="census">

<xsl:variable name="everyone" select="//person"/> <result> <!– Для каждого правнука –>

<xsl:for-each select="$everyone[../../../person]"> <!– Получить прародителя1 –>

<grandparent name="{../../@name}" grandchild="{@name}"/>

<!– Получить прародителя2, если супруг(а) прародителя1 переписан –>

<xsl:if test="../../@spouse">

<grandparent name="{../../@spouse}" grandchild="{@name}"/> </xsl:if>

<!– Получить имена супруга(и) родителя этого человека (то есть отца или матери) –>

<xsl:variable name="spouse-of-parent" select="../@spouse"/> <!– Получить родителей супруга родителя, если переписан –> <xsl:variable name="gp3"

select="$everyone[person/@name=$spouse-of-parent]"/> <xsl:if test="$gp3">

<grandparent name="{$gp3/@name}" grandchild="{@name}"/> <xsl:if test="$gp3/@spouse">

<grandparent name="{$gp3/@spouse}" grandchild="{@name}"/> </xsl:if> </xsl:if> </xsl:for-each> </result> </xsl:template>

Вопрос 6. Найти всех бездетных:

<xsl:strip-space elements="*"/> <xsl:template match="census">

<xsl:variable name="everyone" select="//person"/> <result>

<xsl:for-each select="$everyone[not(./person)]"> <xsl:variable name="spouse"

select="$everyone[@name = current( )/@spouse]"/> <xsl:if test="not ($spouse) or not($spouse/person)">

<xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </result>

</xsl:template>

Вопрос 7. Вывести имена всех потомков Джо. Каждого потомка представить элементом, в котором имя является содержимым, а семейное положение и количество детей — атрибутами. Отсортировать потомков сначала по убыванию количества детей, а потом по имени в алфавитном порядке: <xsl:variable name="everyone" select="//person"/>

<xsl:template match="census"> <result>

<xsl:apply-templates select="//person[@name=’Joe’]"/> </result> </xsl:template>

<xsl:template match="person">

<xsl:variable name="all-desc">

<xsl:call-template name="descendants"> <xsl:with-param name="nodes" select="."/> </xsl:call-template> </xsl:variable>

<xsl:for-each select="exsl:node-set($all-desc)/*">

<xsl:sort select="count(./* | $everyone[@name = current( )/@spouse]/*)" order="descending" data-type="number"/> <xsl:sort select="@name"/> <xsl:variable name="mstatus" select="normalize-space(

substring(‘No Yes’,boolean(@spouse)* 3+1,3))"/> <person married="{$mstatus}"

nkids="{count(./* | $everyone[@name = current( )/@spouse]/*)}"> <xsl:value-of select="@name"/> </person> </xsl:for-each> </xsl:template>

<xsl:template name="descendants"> <xsl:param name="nodes"/>

<xsl:param name="descendants" select="/.."/>

<xsl:choose> <xsl:when test="not($nodes)">

<xsl:copy-of select="$descendants"/> </xsl:when> <xsl:otherwise>

<xsl:call-template name="descendants">

<xsl:with-param name="nodes" select="$nodes[position( ) > 1] |

$nodes[1]/person | id($nodes[1]/@spouse)/person"/> <xsl:with-param name="descendants" select="$descendants | $nodes[1]/person | id($nodes[1]/@spouse)/person"/> </xsl:call-template> </xsl:otherwise> </xsl:choose>

</xsl:template>

Задача решена, но как некрасиво! Сложность связана с тем, что для сортиров­ки приходится собирать всех потомков в набор узлов. Это вынуждает нас пользо­ваться расширением node-set(). К тому же это означает, что функция id() не поможет нам найти супруга, так как она работает только относительно порядка документа. Однако узлы, представляющие потомков, – это копии исходных узлов и потому не принадлежат тому же документу. Из-за этого поиск элементов, отно­сящихся к супругам, превращается в громоздкий обход переменной, содержащей все элементы person. Сравните с решением на языке XQuery:

define function descrip (element $e) returns element

{

let $kids := $e/* union $e/@spouse=>person/*

let $mstatus := if ($e[@spouse]) then "Yes" else "No"

return

<person married={ $mstatus } nkids={ count($kids) }>{ $e/@name/text( ) } </person>

define function descendants (element $e)

{

if (empty($e/* union $e/@spouse=>person/*)) then $e

else $e union descendants($e/* union $e/@spouse=>person/*)

}

descrip(descendants(//person[@name = "Joe"])) sortby(@nkids descending, .)

Обсуждение XSLT 1.0

В отличие от других примеров в настоящей книге, эта глава представляет собой что-то вроде шведского стола. Опрашивать XML можно столь разными способами, что придумать какие-то общие рецепты сложно. Консорциум W3C проделал зас­луживающую уважения работу по классификации запросов, возникающих в разных сферах. Демонстрация того, как эти запросы можно реализовать на XSLT, позво­лит вам самостоятельно найти решения аналогичных задач.

Из-за нехватки места я не включил в эту главу решения на языке XQuery. Тем не менее, сопоставить оба подхода было бы поучительно, поэтому я призываю вас ознакомиться с документом W3C Query Use Case.

Комментировать каждый из приведенных выше примеров было бы непрак­тично. Но большинство читателей, владеющих основами XSLT, скорее всего, пой­мут решения без особого труда. Для некоторых решений возможны более удачные альтернативы. На мой выбор существенное влияние оказали решения на языке XQuery, предложенные в оригинальном документе W3C. Однако я все же пытался применять различные конструкции XSLT, иногда отдавая предпочтение итера­тивному стилю (xsl:for-each), а иногда – декларативному с использованием образцов и команды xsl:apply-templates.

XSLT 2.0

Эта глава получилась довольно объемной, поэтому я не дублировал все решения на XSLT 2.0. Однако приведу несколько задач, на примере которых демонстрируется, как можно улучшить решение за счет использования средств XPath 2.0 и XSLT 2.0:

?               Пользуйтесь командой for-each-group или функцией distinct- values() для устранения дубликатов.

Вопрос 4. Для всех авторов в файле bib.xml вывести имя автора и названия написанных им книг, поместив все это внутрь элемента result:

<xsl:for-each-group select="//author" group-by="."> <result>

<xsl:copy-of select="."/>

<xsl:for-each select="/bib/book[author eq current-grouping-key( )]">

<xsl:copy-of select="title"/> </xsl:for-each> </result> </xsl:for-each-group>

<!– С использованием distinct-values( ) –>

<xsl:for-each select="distinct-values(//author)"> <result>

<xsl:copy-of select="."/>

<xsl:for-each select="/bib/book[author eq .]">

<xsl:copy-of select="title"/> </xsl:for-each> </result> </xsl:for-each>

?               Избегайте копирования узлов, пользуйтесь вместо этого командой

xsl:sequence.

Вопрос 8. В документе books.xml найти названия всех разделов и глав, в которых встречается слово "XML", независимо от уровня вложенности:

<results>

<xsl:sequence select="(//chapter | //section)/ title)[contains(.,’XML’)]"/> </results>

? Для упрощения запросов пользуйтесь функциями и последовательностями.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2 0 01/XMLSchema" xmlns:ckbk="http://www.ora.com/XSLTCkbk">

<xsl:key name="person-key" match="person" use="@name"/>

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:variable name="everyone" select="//person" as="item( )*"/>

<xsl:template match="census"> <result>

<xsl:apply-templates select="//person[@name=’Joe’]"/> </result> </xsl:template>

<xsl:template match="person">

<!– Нет необходимости преобразовывать набор узлов. Воспользуйтесь функцией descendants напрямую –> <xsl:for-each select="ckbk:descendants(.,/)[current( ) != .]">

<xsl:sort select="count(./* | $everyone[@name = current( )/@spouse]/*)" order="descending" data-type="number"/> <xsl:sort select="@name"/>

<xsl:variable name="mstatus" select="if (@spouse) then ‘Yes’ else ‘No’"/> <person married="{$mstatus}"

nkids="{count(./* | key(‘person-key’, @spouse)/*)}"> <xsl:value-of select="@name"/> </person> </xsl:for-each> </xsl:template>

<!– Обратите внимание, насколько эта функция проще шаблона из решения для XSLT 1.0. Мы передаем doc, поскольку внутри функции документ неизвестен. –>

<xsl:function name="ckbk:descendants">

<xsl:param name="nodes" as="item( )*"/> <xsl:param name="doc"/>

<xsl:sequence select="$nodes, for $person in $nodes return ckbk:descendants( ($person/person, key(‘person-key’, $person/@spouse,$doc)/person), $doc)"/> </xsl:function>

</xsl:stylesheet>

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии