|
||||
|
Глава 4Создание образцов выбора До сих пор материал книги был достаточно очевиден, за исключением одного: несколько загадочных образцов выбора (match pattern). Мы работали с различными образцами выбора, такими как «/PLANETS» в элементах <xsl:template>, не предлагая систематического объяснения того, как в действительности работают эти образцы, — как в этом случае: <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> . . . </BODY> </HTML> </xsl:template> В этой главе мы рассмотрим все необходимое, что нужно знать для создания образцов выбора в XSLT. Образцы выбора применяются в элементах <xsl:template>, <xsl:key>и <xsl:number>; с элементом <xsl:template>мы работали с самого начала книги, элемент <xsl:key>мы увидим в главе 9 и элемент <xsl:number>— в главе 5. В частности, для выбора по образцу у элементов <xsl:template>и <xsl:key>используется атрибут match, а у элемента <xsl:number>— атрибуты countи from. Образцы выбора можно также применять в атрибуте selectтаких элементов, как <xsl:apply-templates>, <xsl:value-of>, <xsl:for-each>и <xsl:copy-of>. Здесь важно отметить один момент: атрибут selectэтих элементов обладает большими возможностями, чем атрибуты match, countи fromэлементов <xsl:template>, <xsl:key>и <xsl:number>, потому что в selectможно использовать полные выражения XPath, а не только образцы выбора. Образцы выбора являются подмножеством выражений XPath, то есть все образцы выбора являются допустимыми выражениями XPath, но не все выражения XPath являются образцами выбора. Единственные выражения XPath, которые могут быть образцами, — это выражения, возвращающие набор узлов (даже набор, состоящий из одного узла) и использующие пути, которые задают только дочерние узлы или узлы атрибутов. Образцы выбора определены в самой рекомендации XSLT, в то время как выражения XPath определены в рекомендации XPath (www.w3.org/TR/xpath); тем не менее, эти рекомендации совместимы, потому что все образцы выбора являются одновременно выражениями XPath.
Чтобы еще больше все усложнить, следует сказать, что выражения XPath можно использовать в специальной, необязательной части (и только в этой части) образцов выбора: в предикатах. Как мы увидим в этой главе, предикаты — это выражения XPath, которые вычисляются либо в значения «истина/ложь», либо в числа, заключаемые в квадратные скобки, [ и ]. Например, образец PLANET[NAME="Venus"]выбирает дочерние узлы <PLANET>контекстного узла, у которых есть дочерние узлы <NAME>с текстом «Venus». Выражения внутри [ и ] представляют собой настоящие выражения XPath с известными ограничениями, которые будут рассмотрены в этой главе. Безусловно, для создания образцов выбора необходим опыт, поэтому в данной главе приводится много примеров.
Выбор корневого узлаКак вы уже видели, выбрать корневой узел можно при помощи образца выбора «/», как, например: <xsl:template match="/"> <HTML> <xsl:apply-templates/> </HTML> </xsl:template> Выбор элементовМожно выбирать элементы, просто задавая их имена, как мы уже видели. Следующий шаблон выбирает элементы <PLANETS>: <xsl:template match="PLANETS"> <HTML> <xsl:apply-templates/> </HTML> </xsl:template> Выбор дочерних элементовПри доступе к дочернему узлу определенного узла для разделения имен элементов можно использовать операцию шага /. Пусть, например, требуется создать правило, которое должно применяться только к тем элементам <NAME>, которые являются дочерними для элементов <PLANET>. Для этого можно задать выражение " PLANET/NAME". Вот правило, окружающее текст таких элементов в элемент HTML <Н3>: <xsl:template match="PLANET/NAME"> <H3> <xsl:value-of select="."/> </H3> </xsl:template> Можно также использовать символ * в качестве символа-подстановки, что соответствует любому элементу. (* может выбирать только элементы, однако образец @* выбирает любой атрибут.) Например, следующее правило применяется ко всем элементам <NAME>, которые являются внуками элементов <PLANET>: <xsl:template match="PLANET/*/NAME"> <H3> <xsl:value-of select="."/> </H3> </xsl:template> Выбор потомков элементаВ предыдущем разделе при помощи выражения " PLANET/NAME" я выбирал все элементы <NAME>, являющиеся прямыми потомками элементов <PLANET>, а при помощи выражения " PLANET/*/NAME" — все элементы <NAME>, являющиеся внуками элементов <PLANET>. Есть, однако, более простой способ выполнить оба выбора — применить выражение " PLANET//NAME", выбирающее все элементы <NAME>, находящиеся внутри элементов <PLANET>, независимо от глубины вложенности (соответствующие элементы называются потомками элемента <PLANET>). Иными словами, " PLANET//NAME" выбирает " PLANET/NAME", " PLANET/*/NAME", " PLANET/*/*/NAME" и т.д.: <xsl:template match="PLANETS//NAME"> <H3> <xsl:value-of select="."/> </H3> </xsl:template> Выбор атрибутовКак было показано в главе 3, «Создание и применение шаблонов», можно выбирать атрибуты, если предварять их имена префиксом @. Вы уже работали с атрибутом UNITS, который поддерживают большинство детей элементов <PLANET>: <PLANET> <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> Чтобы извлечь единицы измерения и отобразить их вместе со значениями массы и т.п., можно выбрать атрибут UNITSпри помощи @UNITS(листинг 4.1). Листинг 4.1. Выбор атрибутов <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> . . . </HEAD> <BODY> . . . </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"/></ТD> </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="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Теперь результирующая HTML-таблица включает не только значения, но и их единицы измерения: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE> <TR> . . . <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> </TR> <TR> <TD>Venus</TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> </TR> . . . </TABLE> </BODY> </HTML> Для выбора всех атрибутов элемента можно также использовать подстановку. Например, " PLANET/@*" выбирает все атрибуты элементов <PLANET>. Формальное определение образцов выбораОпределение образцов выбора приводится также в рекомендации XSLT W3C. Образцы выбора определяются в терминах выражений XPath следующим образом: «Синтаксис для образцов является подмножеством для выражений [XPath]. В частности, пути расположения, удовлетворяющие определенным ограничениям, могут использоваться как образцы. Выражение, в то же время являющееся образцом, всегда вычисляется в объект типа набора узлов. Узел удовлетворяет образцу, если узел является членом результата вычисления образца как выражения по отношению к возможному контексту; возможный контекст — это контекст, контекстный узел которого был выбран, или один из его предков». Самое важное предложение в предыдущем абзаце — последнее. Суть в том, что узел X удовлетворяет образцу тогда и только тогда, когда существует узел X или предок X, такой, что при применении к этому узлу образца как выражения XPath, результирующий набор узлов будет включать X. Что в действительности это означает? Это значит, что когда нужно проверить, удовлетворяет ли узел образцу, сначала следует применить образец как выражение XPath к самому узлу, затем применить его последовательно ко всем его предкам, вплоть до корневого узла. Если какой-либо полученный при этом набор узлов будет содержать сам узел, узел удовлетворяет образцу. Такой порядок действий имеет смысл потому, что образцы выбора пишутся для применения к текущему узлу или его дочерним узлам.
W3C дает формальное определение образцов выбора в нотации расширенных форм Бэкуса-Наура (РБНФ), при помощи которой написана и спецификация XML. Объяснение этой грамматики можно найти по адресу www.w3.org/TR/REC-xml (раздел 6). Здесь я привожу формальное определение образцов только для справки. (Разъяснению этого формального определения посвящена целая глава.) В следующем списке приведены используемые здесь лексемы нотации РБНФ: • ::=означает «определяется как»; • +означает «один или больше»; • *означает «ноль или больше»; • |означает «или»; • -означает «не»; • ?означает «необязательно». Далее приведено настоящее, формальное определение образцов выбора W3C; когда элемент заключен в одиночные кавычки, как 'child'или '::', это значит, что элемент должен появиться в образце буквально (как " child::NAME"), — такие элементы называются литералами, Literal: Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern LocationPathPattern ::= '/' RelativePathPattern? | IdKeyPattern ('/' | '//') RelativePathPattern? | '//'? RelativePathPattern IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal '.' Literal ')' RelativePathPattern ::= StepPattern | RelativePathPattern '/' StepPattern | RelativePathPattern '//' StepPattern StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier | ('child' | 'attribute') '::' Определения NodeText (текстового узла) и Predicate (предиката) приводятся в спецификации XPath ( Exprсоответствует выражению XPath, a NCNameи QNameбыли определены в начале главы 2, «Создание и применение таблиц стилей»): NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' Predicate ::= '[' PredicateExpr ']' PredicateExpr ::= Expr AbbreviatedAxisSpecifier ::= '@'? NameTest :: = '*' | NCName ':' '*' | QName NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node' Как вы можете видеть, все это больше походит на какой-то код. Давайте начнем его расшифровывать. Во-первых, образец (pattern) состоит из одного (или более) образца пути расположения (location path pattern). Образец пути расположения, в свою очередь, состоит из одного или нескольких образцов шага (step pattern), разделенных / или //, или одним (несколькими) образцом шага в объединении с функциями idи key(выбирающими элементы с определенными идентификаторами или ключами). Образцы шага являются строительными блоками шаблонов: в одном пути можно использовать несколько шагов, разделяя их символами / или //, как в образце " PLANET/*/ NAME", в котором три шага: " PLANET", " *" и " NAME". Если вы начнете сам образец с символа /, он будет называться абсолютным, так как вы указали образец от корневого узла (как в " /PLANETS/PLANET" или " //PLANET"); иначе образец называется относительным и применяется начиная с контекстного узла (как в " PLANET"). Затем образец шага состоит из оси, условия узла и предикатов (которых может и не быть). Например, в выражении child::PLANET[position()=5], child— это имя оси, PLANET— условие узла, a [position()=5]— это предикат. (Предикаты всегда заключены в квадратные скобки.) Образцы можно создавать при помощи одного или более образцов шага, как, например, образец /child::PLANET/child::NAME, который выбирает элементы <NAME>, дочерние по отношению к родителю <PLANET>. Таким образом, чтобы понять работу образцов, вам необходимо понять работу образцов шага, поскольку образцы состоят из одного или более образцов шага, в таких выражениях, как " step-pattern1/step-pattern2/step-pattern3…". А чтобы понять работу образца шага, необходимо понять работу деятельности трех составных частей — осей, условий узлов и предикатов, которыми мы и займемся в следующих разделах. Образцы шага, часть 1: оси образцаОси — первая часть образцов шага. Например, в образце шага child::NAME, ссылающемся на элемент <NAME>, дочерний по отношению к контекстному узлу, childназывается осью. У образцов две оси: • ось attributeсодержит атрибуты контекстного узла; • ось childсодержит детей контекстного узла. Если ось явно не задана, ось childбудет осью по умолчанию. При помощи осей можно задать шаг расположения (location path) или путь, как в следующем примере, в котором ось childиспользуется для задания выбора дочерних узлов контекстного узла, элемента <PLANET>: <xsl:template match="PLANET"> <HTML> <CENTER> <xsl:value-of select="child::NAME"/> </CENTER> <CENTER> <xsl:value-of select="child::MASS"/> </CENTER> <CENTER> <xsl:value-of select="child::DAY"/> </CENTER> </HTML> </xsl:template> Рассмотрим ряд примеров применения осей: • child::PLANET. Возвращает дочерние элементы <PLANET>контекстного узла; • child::*. Возвращает все дочерние элементы контекстного узла (* выбирает только элементы); • attribute::UNITS. Возвращает атрибут UNITSконтекстного узла; • child::*/child::PLANET. Возвращает всех внуков <PLANET>контекстного узла. Хотя, судя по этим примерам, кажется, что можно применять только оси детей и атрибутов, на практике это не совсем так. Когда требуется указать детей, возможности оси childнесколько ограничены, потому что необходимо указывать каждый уровень, который необходимо выбрать — например " child::PLANETS/child::PLANET/child::MASS" выбирает элемент <MASS>, дочерний по отношению к элементу <PLANET>, который, в свою очередь, дочерний по отношению к <PLANETS>. Если вам требуется выбрать все элементы <MASS>, появляющиеся в любом месте элемента <PLANETS>, детей, внуков, правнуков и т.д., кажется, что нет способа сделать это в одном образце. В XPath это можно сделать при помощи выражения наподобие " child::PLANETS/descendant::MASS", но в образцах нельзя использовать ось потомков (descendant). Помните, однако, что в этих же целях можно применить операцию //. Например, образец " child::PLANETS//child::MASS" выбирает все элементы <MASS>в любом месте внутри элемента <PLANETS>. Следующий пример (листинг 4.2) демонстрирует работу этого образца, заменяя текст во всех элементах <MASS>независимо от того, где они находятся внутри элемента <PLANETS>, на текст " Very heavy!". Для того чтобы скопировать в результирующий XML-документ все остальные узлы planets.xml, я также установил правило, выбирающее любой узел при помощи условия узла (node test) node, с которым мы познакомимся позже. Заметьте, что, хотя образец, выбирающий любой узел, также выбирает все элементы <MASS>, образец " child::PLANETS//child::MASS" гораздо более специален — поэтому, как объяснялось в главе 3, процессор XSLT задаст ему более высокий приоритет для элементов <MASS>. Листинг 4.2. Выбор элементов <MASS> <?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="child::PLANETS//child::MASS"> <MASS> Very heavy! </MASS> </xsl:template> </xsl:stylesheet> А вот результирующий XML-документ: <?xml version="1.0" encoding-"UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET> <NAME>Mercury</NAME> <MASS> Very heavy! </MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--В перигелии-> </PLANET> <PLANET> <NAME>Venus</NAME> <MASS> Very heavy! </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><!--B перигелии--> </PLANET> <PLANET> <NAME>Earth</NAME> <MASS> Very heavy! </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> При задании осей в образцах можно воспользоваться рядом сокращений, применяемых практически повсеместно. Сокращенный синтаксисДля образцов существует два правила сокращения осей: • child::childnameможет быть сокращено как childname; • attribute::childnameможет быть сокращено как @childname. В следующем списке перечислен ряд примеров образцов с сокращенным синтаксисом; в конце главы вы увидите много других. • PLANET. Выбирает дочерние элементы <PLANET>контекстного узла; • *. Выбирает все дочерние элементы контекстного узла; • @UNITS. Выбирает атрибут UNITSузла; • @*. Выбирает все атрибуты контекстного узла; • */PLANET. Выбирает всех внуков <PLANET>контекстного узла; • //PLANET. Выбирает всех потомков <PLANET>корня документа; • PLANETS//PLANET. Выбирает все элементы <PLANET>, являющиеся потомками дочерних элементов <PLANETS>контекстного узла; • //PLANET/NAME. Выбирает все элементы <NAME>, дочерние по отношению к <PLANET>; • PLANET[NAME]. Выбирает детей <PLANET>контекстного узла, у которых есть дочерние элементы <NAME>. В таком образце, как " child::PLANET", " child" является осью, a " PLANET" — условием узла, что представляет собой вторую часть образцов шага. Образцы шага, часть 2: условия узлаУсловия узла (node test) составляют вторую часть образцов шага. В качестве условий узла можно использовать названия узлов или символ подстановки *для выбора и узлов, и их типов. Например, выражение child::*/child::NAMEвыбирает все элементы <NAME>, являющиеся правнуками контекстного узла. Помимо названий узлов и символа подстановки, можно применять также следующие условия узлов: • comment()выбирает узлы комментария; • node()выбирает узел любого типа; • processing-instruction()выбирает узел инструкции обработки. В скобках можно указать название выбираемой инструкции обработки; • text()выбирает текстовый узел. В следующих разделах мы изучим эти условия узлов и рассмотрим примеры их применения. Выбор комментариевТекст комментариев можно выбрать при помощи образца comment(). Разумеется, не следует хранить данные, которые попадут в выходной документ, в комментариях входного документа. Тем не менее, вам может потребоваться преобразовать комментарии из формы <!--comment-->в какую-то другую форму, используемую другим языком разметки, — например, элемент <COMMENT>. В следующем примере я извлеку комментарии из planet.xmlи включу их в полученные выходные данные. <PLANET> <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><!--B перигелии--> </PLANET> Чтобы извлечь комментарии и поместить их в элементы <COMMENT>, я включил правило только для комментариев (листинг 4.3). Листинг 4.3. Выбор комментариев <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="PLANETS"> <HTML> <xsl:apply-templates/> </HTML> </xsl:template> <xsl:template match="comment()"> <COMMENT> <xsl:value-of select="."/> </COMMENT> </xsl:template> </xsl:stylesheet> Вот результат для Венеры, в котором комментарий преобразован в элемент <COMMENT>: Venus .815 116.75 3716 .943 66.8<COMMENT>B перигелии</СОММЕNT> Обратите внимание: здесь текст для других элементов в элементе <PLANET>также включается в выходной документ, потому что так установлено в соответствии с правилом по умолчанию для каждого элемента. Поскольку для элементов я не предоставил какого-либо правила, их текст просто включается в выходной документ. Выбор узлов при помощи node()В образце условие узла nodeвыбирает любой узел, за исключением корневого узла — помните, что в действительности это child::node(). Предположим, мы хотим создать таблицу стилей, копирующую произвольный документ XML, используя <xsl:copy>. (В главе 3 для этого применялся элемент <xsl:copy-of>.) Можно начать так, как показано в следующем примере. В этом случае в применяемом шаблоне для выбора любого элемента или любого атрибута используется операция OR, с которой мы познакомимся позже в этой главе (этот шаблон фактически выбирает себя — для того чтобы продолжать копирование на много уровней вглубь): <?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="@*|*"> <xsl:copy> <xsl:apply-templates select="@*|*"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Однако посмотрите на результат — обратите внимание на то, что в этой версии, выбирающей только элементы и атрибуты ( @*|*), не копируются узлы-разделители и текстовые узлы: <?xml version="1.0" encoding="UTF-8"?> <PLANETS><PLANET><NAME/><MASS UNITS="(Earth = 1)"/><DAY UNITS="days"/><RADIUS UNITS="miles"/><DENSITY UNITS="(Earth = 1)"/><DISTANCE UNITS="million miles"/></PLANET><PLANET><NAME/><MASS UNITS="(Earth = 1)"/><DAY UNITS="days"/><RADIUS UNITS="miles"/><DENSITY UNITS="(Earth = 1)"/><DISTANCE UNITS="million miles"/></PLANET><PLANET><NAME/><MASS UNITS="(Earth = 1)"/><DAY UNITS="days"/><RADIUS UNITS="miles"/><DENSITY UNITS="(Earth = 1)"/><DISTANCE UNITS="million miles"/> </PLANET></PLANETS> Это, конечно, неполно. Если я, с другой стороны, буду выбирать по образцу " @*|node()" вместо " @*|*", новое правило шаблона выберет все узлы за исключением корневого узла (который создается в результирующем дереве автоматически), поэтому символы-разделители будут скопированы, так же как и текст (листинг 4.4). Листинг 4.4. Копирующая таблица стилей <?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Новый результат: <?xml version="1.0" encoding="UTF-8"?> <?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> <PLANET> <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><!--B перигелии-> </PLANET> . . . Выбор текстовых узлов при помощи text()Выбрать текст узла можно при помощи образца " text()". Как правило, нет особых причин применять условие узла text. В XSLT существует правило по умолчанию, в соответствии с которым текст текстового узла включается в выходной документ, если этот узел не выбирается какими-либо другими правилами. Если нужно сделать это правило по умолчанию явным, можно поступить, например, так: <xsl:template match="text()"> <xsl:value-of select="."/> </xsl:template> Можно перекрыть это правило, не отправляя текст из текстовых узлов в выходной документ, — в том числе так: <xsl:template match="text()"> </xsl:template> Потребность в применении условия текстового узла возникает, например, когда нужно выбрать узлы с определенным текстом. Предикат " NAME[text()='Venus']" выбирает элементы <NAME>, в которых содержится имя " Venus". (Будьте внимательны с вложением кавычек, чтобы процессор XSLT не ошибся, — например, такой предикат не работает: " NAME[text()="Venus"]".) Еще одна причина для использования условия текстового узла появляется, когда требуется применить к текстовым узлам некоторое условие при помощи строковых функций XPath (которые будут рассмотрены позже в этой главе). Например, текстовый узел " Earth" в <NAME>Earth</NAME>выбирается образцом " text()[starts-with(.,'Е')]".
Выбор инструкций обработкиДля выбора инструкций обработки используйте образец processing-instruction(): <xsl:template match="/processing-instruction()"> <I> Found a processing instruction. </I> </xsl:template> Можно также указать, какую именно инструкцию обработки вы хотите выбрать, задав имя инструкции (исключая <?и ?>), — как в следующем примере, в котором выбирается инструкция обработки <?xml-include?>: <xsl:template match-"/processing-instruction(xml-include)"> <I> Found an xml-include processing instruction. </I> </xsl:template>
На этом мы заканчиваем обсуждение возможных условий узлов в образцах шага. Третья и последняя часть образцов шага — предикаты Образцы шага, часть 3: предикатыПредикаты, третья часть образцов шага, содержат выражения XPath. Предикат можно заключить в операцию [] и проверить, верно ли заданное условие. Например, можно проверить: • значение атрибута в заданной строке; • значение элемента; • содержит ли элемент определенного ребенка, атрибут или другой элемент; • позицию узла в дереве узлов. Подробнее выражения XPath обсуждаются в главе 7, но здесь будет представлено введение в тему, так как эти выражения можно применять в предикатах образцов. Выражения XPath более сложны, чем образцы выбора. Если при их создании у вас возникнут затруднения, вам может помочь удобная программа-пример ApplyXPath.java из пакета Xalan, при помощи которой можно применить к документу выражение XPath и посмотреть на результаты. Например, если применить выражение XPath " PLANET/NAME" к planets.xml, будут отображены значения всех элементов <NAME>, дочерних по отношению к элементам <PLANET>(открывающий и закрывающий теги <output>добавляются программой ApplyXPath): C:\>java ApplyXPath planets.xml PLANET/NAME <output> <NAME>Mercury</NAME> <NAME>Venus</NAME> <NAME>Earth</NAME> </output> Если предикат имеет числовое значение, последнее представляет условие позиции (position test). Например, NAME[1] выбирает первого ребенка <NAME>контекстного узла. Условия позиции W3C, а также условия позиции в Xalan, Oracle, XT, Saxon и MSXML3 (XML процессор от Microsoft, подразумевающий использование JavaScript, с которым вы встречались в главе 1 и еще встретитесь в главе 10, «Работа с API процессоров XSLT») основаны на 1, поэтому первый ребенок — это ребенок 1. Условия позиции в XML-документах, которые используют таблицы стилей XSL и загружаются в текущую версию Internet Explorer (версию 5.5, на смену которой приходит 6.0), основаны на 0 (и в предикатах можно использовать только очень сокращенную форму выражений XPath) — и, следовательно, так же считается в большей части документации по XSL на web-узле Microsoft. В другом случае значением предиката должна быть ложь или истина, что называется логическим условием (Boolean test). Например, предикат [@UNITS="million miles"]выбирает элементы, у которых имеются атрибуты UNITSсо значением " million miles". Предикаты являются полными выражениями XPath, хотя на предикаты, используемые в образцах, накладывается два ограничения: • когда образец используется в атрибуте match, предикат не должен содержать никаких ссылок на переменные XSL (которые обсуждаются в главе 9). Это ограничение не применяется к предикатам, используемым в элементах <xsl:number>; • образцы не могут использовать в предикатах функцию XPath current, возвращающую текущий узел. Ее применение ограничено, поэтому обработка зависит от реализации и не зависит от текущего состояния обработки. В следующем примере образец выбирает элементы <PLANET>с дочерними элементами <NAME>: <xsl:template match="PLANET[NAME]"> . . . </xsl:template> Этот образец выбирает любой элемент с дочерним элементом <NAME>: <xsl:template match="*[NAME]"> . . . </xsl:template> Теперь я задал элементам <PLANET>в planets.xmlновый атрибут, COLOR, устанавливающий цвет планеты: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET COLOR="RED"> <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><!--В перигелии--> </PLANET> <PLANET COLOR="WHITE"> <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><!--B перигелии--> </PLANET> <PLANET COLOR="BLUE"> <NAME>Earth</NAME> <MASS UNITS="(Earth = 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> Следующее выражение выбирает элементы <PLANET>с атрибутом COLOR: <xsl:template match="PLANET[@COLOR]"> . . . </xsl:template> А что, если нам требуется выбрать планеты, у которых атрибут COLORимеет значение " BLUE" (голубой)? Это можно сделать при помощи операции =, как показано в листинге 4.5. Листинг 4.5. Применение операции = <"xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="PLANETS"> <HTML> <xsl:apply-templates/> </HTML> </xsl:template> <xsl:template match="PLANET[@COLOR = 'BLUE']"> The <xsl:value-of select="NAME"/> is blue. </xsl:template> <xsl:template match="text()"> </xsl:template> </xsl:stylesheet> Таблица стилей из листинга 4.5 отбирает планеты с голубым цветом и убирает остальные, выключая правило по умолчанию для текстовых узлов. Результат следующий: <HTML> The Earth is blue. </HTML> Создание предикатовПредикаты — настоящие выражения XPath, и XPath гораздо ближе к настоящему языку, чем образцы: к примеру, выражения XPath могут возвращать не только списки узлов, но также логические, строковые и числовые значения. Выражения XPath могут работать не только с текущим узлом или дочерними узлами: можно работать с родительскими узлами, узлами-предками и другими узлами. Глава 7 полностью посвящена XPath, но имеет смысл предоставить введение в предмет здесь, при обсуждении образцов, потому что часть предиката образца обладает наибольшими возможностями. В предикатах могут быть все виды выражений; в следующем списке перечислен ряд возможных типов, которые будут изучены в следующих разделах: • наборы узлов; • логические выражения; • числа; • строки. Предикаты: наборы узловНабор узлов (node set), как понятно из названия, представляет собой просто совокупность узлов (и может содержать только один узел). Выражение child::PLANETвозвращает набор узлов, состоящий из всех элементов <PLANET>. Выражение child::PLANET/child::NAMEвозвращает список узлов, состоящий из всех элементов <NAME>, дочерних по отношению к элементам <PLANET>. Для выбора узла или узлов из набора узлов воспользуйтесь следующими функциями для работы с наборами узлов в предикатах: • last(). Возвращает количество узлов в наборе узлов; • position(). Возвращает позицию контекстного узла в контекстном наборе узлов (начиная с 1); • count(node-set). Возвращает количество узлов в наборе. Если опустить node-set, функция будет применена к контекстному узлу; • id(string ID). Возвращает набор узлов, содержащий элемент с ID, удовлетворяющим переданной функции строке, или пустой набор узлов, если такой элемент отсутствует. Можно перечислить несколько идентификаторов, разделенных символами-разделителями, — тогда функция вернет набор узлов, состоящий из элементов с этими идентификаторами; • local-name(node-set). Возвращает локальное имя первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу; • namespace-uri(node-set). Возвращает URI пространства имен первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу; • name(node-set). Возвращает полностью определенное имя первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу. В листинге 4.6 я перенумеровал элементы в выходном документе при помощи функции position(). Листинг 4.6. Применение функции position <?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 </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <P> <xsl:value-of select="position()"/> <xsl:text> </xsl:text> <xsl:value-of select="NAME/> </P> </xsl:template> </xsl:stylesheet> Вот результат. Как видите, планеты перенумерованы: <HTML> <HEAD> <TITLE> The Planets </TITLE> </HEAD> <BODY> <P> 1. Mercury </P> <P> 2. Venus </P> <P> 3. Earth </P> </BODY> </HTML> Можно также применять функции для работы с наборами узлов в предикатах, как, например, PLANET[position()=last()], выбирающая последнего ребенка <PLANET>контекстного узла Предикаты: логические значенияВ выражениях XPath можно также использовать логические (Boolean) значения. Для чисел ноль принимается за ложь (false), другие значения — за истину (true). Пустая строка, "", также считается ложью, все остальные строки — истиной. Для вычисления логических результатов true/false можно применять следующие логические операции XPath: • != означает «не равно»; • < означает «меньше, чем» (в документах XML или XSL используйте <); • <= означает «меньше или равно» (в документах XML или XSL используйте <=); • = означает «равно» (программисты на С, С++, Java и JavaScript, обратите внимание: эта операция пишется как один знак =, а не два); • > означает «больше, чем»; • >= означает «больше или равно».
Для связи логических выражений логическими операциями And и Or используются ключевые слова and и or; слово not инвертирует логический смысл выражения — с истины на ложь или со лжи на истину. В листинге 4.7 я определяю элемент <PLANET>Земли и помещаю в таблицу строки " Earth", " needs", " no" и " introduction" вместо числовых данных Земли. Я определяю, которая из планет есть Земля, при помощи предиката "[NAME='Earth']", проверяющего значение элемента <NAME>, которое, в свою очередь, представляет собой заключенный в элементе текст. Я также предоставил шаблон для других планет, удовлетворяющих предикату " [NAME!='Earth']''. Листинг 4.7. Определение планеты Земля <?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/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET[NAME='Earth']"> <TR> <TD>Earth</TD> <TD>needs</TD> <TD>no</TD> <TD>introduction.</TD> </TR> </xsl:template> <xsl:template match="PLANET[NAME!='Earth']"> <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="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet> Вот результат: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <Н1> The Planets Table </Н1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> . . . <TR> <TD>Earth</TD> <TD>needs</TD> <TD>no</TD> <TD>introduction.</TD> </TR> </TABLE> </BODY> </HTML> Результат можно увидеть на рис. 4.1. Рис. 4.1. Применение предикатов XPath В следующем примере используется логическая операция >. Это правило применяется ко всем элементам <PLANET>после позиции 5: <xsl:template match="PLANET[position() > 5]"> <xsl:value-of select="."/> </xsl:template> Имеется также функция true, всегда возвращающая значение true, и функция false, всегда возвращающая значение false. Функция notинвертирует логический смысл выражения, как в следующем случае, где я выбираю все элементы <PLANET>, кроме последнего: <xsl:template match="PLANET[not(position() = last())]"> <xsl:value-of select="."/> </xsl:template> Наконец, функция langвозвращает истину или ложь в зависимости от того, является ли язык контекстного узла (определяемый атрибутами xml:lang) таким же, как язык, который передан в эту функцию. Предикаты: числаВ XPath числа хранятся в формате числа с плавающей точкой двойной точности. (Технически все числа XPath хранятся в 64-разрядном формате IEEE числа с плавающей точкой двойной точности, floating-point double.) Все числа хранятся как числа с двойной точностью — даже целые числа, как 5 в рассматриваемом примере: <xsl:template match="PLANET[position() > 5]"> <xsl:value-of select="."/> </xsl:template> Над числами можно производить ряд операций: • +сложение; • -вычитание; • *умножение; • divделение (символ /, соответствующий делению в других языках, в XML, XSL и XPath уже занят); • modвозвращает значение деления по модулю двух чисел (остаток после деления первого числа на второе). Например, элемент <xsl:value-of select="180+420"/>вставит в выходной документ строку " 600". В следующем примере выбираются все планеты, у которых отношение дня (измеренного в днях Земли) к массе (где масса Земли принята за 1) больше 100: <xsl:template match="PLANETS"> <HTML> <BODY> <xsl:apply-templates select="PLANET[DAY div MASS > 100]"/> </BODY> </HTML> </xsl:template> XPath также поддерживает следующие функции работы с числами: • ceiling(). Возвращает наименьшее целое, большее, чем переданное функции число; • floor(). Возвращает наибольшее целое, меньшее, чем переданное функции число; • round(). Округляет переданное число до ближайшего целого; • sum(). Возвращает сумму переданных функции чисел. Например, среднюю массу планет в planets.xmlможно найти так, как в листинге 4.8: Листинг 4.8. Вычисление средней массы планет <?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)"/> </BODY> </HTML> </xsl:template> </xsl:stylesheet> СтрокиВ XPath строки формируются из символов Unicode, как можно было предположить. Ряд функций специально предназначен для работы со строками: • string(object object1). Преобразует объект в строку; • starts-with(string string1, string string2). Возвращает истину, если первая строка начинается (starts with) со второй строки; • contains(string string1, string string2). Возвращает истину, если первая строка содержит (contains) вторую строку; • substring(string string1, number offset number length). Возвращает lengthсимволов из строки, начиная со смещения offset; • substring-before(string string1, string string2). Возвращает часть строки string1до первого вхождения строки string2; • substring-after(string string1, string string2). Возвращает часть строки string1после первого вхождения string2; • string-length(string string1). Возвращает количество символов в строке string1; • normalize-space(string string1). Возвращает строку string1после отбрасывания лидирующих и завершающих символов-разделителей и замены нескольких последовательных разделителей на один пробел; • translate(string string1, string string2, string string3). Возвращает строку string1, в которой все вхождения символов в строке string2заменены на соответствующие символы в строке string3; • concat(string string1, string string2, ...). Возвращает конкатенацию (объединение) всех строк. Есть еще одна строковая функция, о которой вам следует знать, входящая не в XPath, а в XSLT: • format-number(number number1, string string2, string string3). Возвращает строку, содержащую число number1в виде форматированной строки, используя string2в качестве форматирующей строки (форматирующие строки создаются так же, как для метода Java java.text.DecimalFormat) и string3как возможную строку локализации. В листинге 4.9 я выбираю текстовые узлы, в которых текст начинается с 'Е', чтобы выбрать Earth (Земля), и добавляю текст '(the World)' (мир), получая 'Earth (the World)'. Для этого я применяю предикат " text()[starts-with(., 'Е')]". Листинг 4.9. Применение функции starts-with <?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"/></ТD> </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> Этот документ показан на рис. 4.2. Рис. 4.2. Применение текстовых предикатов Предикаты: фрагменты результирующего дереваXSLT 1.0 добавляет к поддерживаемым XPath типам данных фрагменты результирующего дерева. Это фрагменты дерева, которые можно присваивать переменным XSLT, они не очень широко распространены. Практически все, что можно с ними сделать, — это вычислить их строковое значение. В рабочем проекте XSLT 1.1 их поддержка была удалена, поэтому, видимо, в XSLT 2.0 их уже не будет. Сокращенный синтаксис предикатовВыражения предикатов можно сокращать, опуская " position()=". Например, [position()=3]становится [3], [position()=last()]становится [last()]и т.д. С использованием сокращенного синтаксиса применять выражения XPath в предикатах становится существенно проще. Вот ряд примеров: • PLANET[2]. Возвращает второго ребенка <PLANET>контекстного узла; • PLANET[last()]. Возвращает последнего ребенка <PLANET>контекстного узла; • /PLANETS/PLANET[2]/NAME[1]. Возвращает первый элемент <NAME>второго элемента <PLANET>элемента <PLANETS>; • PLANET[5][@UNITS="million miles"]. Возвращает пятого ребенка <PLANET>контекстного узла, только если у него имеется атрибут UNITSсо значением " million miles". Это выражение можно также написать как PLANET[@UNITS="million miles"][5]. На этом мы заканчиваем рассмотрение трех частей образцов шага: осей, условий узлов и предикатов. Это строительные блоки образцов выбора. Лучше всего изучить создание образцов на примере, и многие примеры мы вскоре рассмотрим. Сначала, однако, важно рассмотреть две небольшие темы. Как вы помните из формального определения образцов выбора, можно помимо образцов шага, создавать образцы, выбирающие элементы по идентификатору (ID) или ключу. Выбор по IDВ дополнение к созданию образцов из образцов шага, задающих ось, условие узла и предикат, можно применять и образец id()для выбора элементов с определенным значением ID. Для работы с этим образцом необходимо задать элементам атрибут ID, который должен быть объявлен с типом ID, что можно сделать в DTD или схеме документа. В следующем примере правило добавляет текст всех элементов, имеющих ID " favorite": <xsl:template match = "id('favorite')"> <H3><xsl:value-of select="."/></H3> </xsl:template> Вот как может выглядеть объявление DTD для planets.xml, в котором объявляется ID и его значение устанавливается в " favorite": <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="#stylesheet"?> <!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, потому что они не читают DDS или схему XML. (Возможность доступа к информации ID должна быть включена в XSLT 2.0.) Но есть альтернатива: можно осуществлять выбор по ключу.
Выбор по ключуКлючи дают простой способ идентифицировать элементы; конкретные ключи можно выбрать при помощи образца " key()". Работа с ключами подробно обсуждается в главе 9, но здесь я также приведу небольшой пример. Для создания ключа служит элемент <xsl:key>. Это элемент верхнего уровня, поэтому он используется вне шаблонов и как дочерний элемент <xsl:stylesheet>. В следующем примере я при помощи ключа выбираю планеты, чей атрибут 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> Теперь я могу создать ключ с именем 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> <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="key('COLOR', 'BLUE')"/> </TABLE> </BODY> </HTML> </xsl:template> . . . А вот результат — как видите, единственной планетой, удовлетворявшей используемому образцу, была Земля (Earth). <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</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> Применение операции OrПри помощи операции Or (или), |, можно осуществлять выбор по нескольким возможным образцам, что очень удобно с ростом сложности документов. В следующем примере я хочу отобразить элементы <NAME>и <MASS>полужирным, что задается тегом HTML <В>. Для выбора либо элементов <NAME>, либо <MASS>в новом правиле я использую операцию Or: <?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="NAME | MASS"> <B> <xsl:apply-templates/> </B> </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> The Planets Table </TITLE> </HEAD> <BODY> . . . <TR> <TD><B>Mercury</B></TD> <TD><B>.0553</B></TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> <TR> <TD><B>Venus</B></TD> <TD><B>.815</B></TD> <TD>3716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD><B>Earth</B></TD> <TD><B>1</B></TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> Операцию | можно применять к любым допустимым образцам — например, в таких выражениях, как " PLANET|PLANET//NAME"; можно использовать несколько операций | — например, " NAME|MASS|DAY" и т.п. Примеры образцовИзучать образцы лучше всего на примерах. Предположим, что нам требуется преобразовать planets.xmlв planets.html, но сохранить только первую планету, Меркурий. Это можно сделать при помощи предиката [position()<2], так как позиция первой планеты равна 1, следующей — 2, и т.д. Заметьте, однако, что <; является управляющим символом для процессоров XSLT, начинающим разметку, поэтому вместо < необходимо использовать <. И отметьте, что для того чтобы убрать из planets.xmlдругие элементы, для них нужно предоставить пустой шаблон, что можно сделать при помощи предиката [position()>=2](листинг 4.10). Листинг 4.10. Выбор только Меркурия <?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/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET[position() < 2]"> <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[position() >= 2]"> </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> 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> В следующем примере в элемент <PLANET>Земли были добавлены атрибуты COLOR(цвет) и POPULATED(населена): <PLANET COLOR="blue" POPULATED="yes"> <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">1284</DISTANCE><!--B перигелии--> </PLANET> Как выбрать только элементы, имеющие оба атрибута, COLORи POPULATED? Можно применить предикат " [@COLOR and @POPULATED]". Чтобы убрать другие элементы — так, чтобы правило по умолчанию не поместило их текст в результирующий документ, — можно применить предикат " [not(@COLOR) or not(@POPULATED)]", как показано в листинге 4.11. Листинг 4.11. Выбор только элементов с двумя атрибутами COLOR и POPULATED <?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> 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> <xsl:apply-templates/> </TABLE> </BODY> </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> Этот документ показан на рис. 4.3. Рис. 4.3. Применение предикатов XPath для проверки атрибутов В следующем примере я копирую planets.xmlв новый XML-документ и изменяю текст в элементе <NAME>Венеры на " The Planet of Love" (планета любви). Для этого я сначала копирую в результирующий документ все узлы и атрибуты: <?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> . . . Теперь я добавлю новое правило, выбирающее элементы <NAME>с текстом " Venus" (Венера) по образцу " NAME[text()='Venus']". Хотя элементы <NAME>удовлетворяют обоим правилам этой таблицы стилей, правило с образцом " NAME[text()='Venus']" осуществляет более узкий выбор, поэтому для элемента <NAME>Венеры процессор XSLT применит его: <?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="NAME[text() = 'Venus']"> <NAME> The Planet of Love </NAME> </xsl:template> </xsl:stylesheet> И вот результат: <?xml version="1.0" encoding="utf-8"?> <?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> <PLANET> <NAME> The 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><!--B перигелии--> </PLANET> <PLANET> <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> В действительности, в выражениях XPath можно ссылаться на контекстный узел при помощи ".", и значением по умолчанию для узла является его текст, поэтому следующее правило работает точно так же: <?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="NAME[. = 'Venus']"> <NAME> The Planet of Love </NAME> </xsl:template> </xsl:stylesheet> Имеет смысл привести как можно больше примеров — примеров XPath или образцов выбора никогда не бывает слишком много. Ниже приведен содержательный ряд примеров образцов выбора: • PLANETвыбирает дочерние элементы <PLANET>контекстного узла; • /PLANETSвыбирает корневой элемент <PLANETS>документа; • *выбирает все дочерние элементы контекстного узла; • PLANET[3]выбирает третьего ребенка <PLANET>контекстного узла; • PLANET[last()]выбирает последнего ребенка <PLANET>контекстного узла; • PLANET[NAME]выбирает детей <PLANET>контекстного узла, имеющих детей <NAME>; • PLANET[DISTANCE]/NAMEвыбирает все элементы <NAME>элементов <PLANET>, содержащих по крайней мере один элемент <DISTANCE>; • PLANET[DISTANCE]/PLANET[DAY]выбирает все элементы <PLANET>элементов <PLANET>, в которых элемент <PLANET>содержит по крайней мере один элемент <DISTANCE>, и элемент <PLANET>содержит по крайней мере один элемент <DAY>; • РLANETS[РLАNET/DAY]выбирает все элементы <PLANETS>, содержащие элементы <PLANET>как минимум с одним элементом <DAY>; • PLANET[DISTANCE][NAME]выбирает все элементы <PLANET>, имеющие элементы <DISTANCE>и <NAME>; • PLANETS/PLANET[last()]выбирает последний элемент <PLANET>в каждом элементе <PLANETS>; • */PLANETвыбирает всех внуков <PLANET>контекстного узла; • /PLANETS/PLANET[3]/NAME[2]выбирает второй элемент <NAME>третьего элемента <PLANET>элемента <PLANETS>; • //PLANETвыбирает всех потомков <PLANET>корня документа; • PLANETS//PLANЕТвыбирает потомков элемента <PLANET>детей элемента <PLANETS>контекстного узла; • //PLANET/NAMEвыбирает все элементы <NAME>, дочерние по отношению к родителю <PLANET>; • РLАNETS//PLАNET/DISTАNСЕ//РЕRIНЕLIONвыбирает элементы <PERIHELION>везде внутри элемента <DISTANCE>элемента <PLANET>, везде внутри элемента <PLANETS>; • @UNITSвыбирает атрибут UNITSконтекстного узла; • @*выбирает все атрибуты контекстного узла; • *[@UNITS]выбирает все элементы с атрибутом UNITS; • DENSITY/@UNITSвыбирает атрибут UNITSв элементах <DENSITY>; • PLANET[not(@COLOR) or not(@SIZE)]выбирает элементы <PLANET>, не имеющие обоих атрибутов COLORи SIZE; • PLANETS[@STAR="Sun"]//DENSITYвыбирает любой элемент <DENSITY>с элементом-предком <PLANETS>, имеющим атрибут STARсо значением " Sun"; • PLANET[NAME="Venus"]выбирает детей <PLANET>контекстного узла, имеющих детей <NAME>с текстом " Venus"; • PLANET[NAME[1]="Venus"]выбирает все элементы <PLANET>, у которых в первом элементе <NAME>содержится текст в " Venus"; • DAY[@UNITS!="million miles"]выбирает все элементы <PLANET>, атрибут UNITSкоторых не равен " million miles"; • PLANET[@UNITS="days"]выбирает всех детей <PLANET>контекстного узла, имеющих атрибут UNITSсо значением " days"; • PLANET[6][@UNITS="days"]выбирает шестого ребенка <PLANET>контекстного узла, только если у этого ребенка есть атрибут UNITSсо значением " days", — что также можно записать как PLANET[@UNITS="days"][6]; • PLANET[@COLOR and @UNITS]выбирает всех детей <PLANET>контекстного узла, имеющих оба атрибута COLORи UNITS; • *[1][NAME]выбирает любой элемент <NAME>, являющийся первым ребенком своего родителя; • *[position() < 5]выбирает первые пять детей контекстного узла; • *[position() < 5][@UNIT]выбирает первые пять детей контекстного узла с атрибутом UNITS; • text()выбирает все дочерние текстовые узлы контекстного узла; • text()[starts-with(., "In the course of human events")]выбирает все дочерние текстовые узлы контекстного узла, начинающиеся с " In the course of human events"; • /PLANETS[@UNITS="million miles"]выбирает все элементы PLANETS, у которых значение атрибута UNITSравно " million miles"; • PLANET[/PLANETS/@UNITS=@REFERENCE]выбирает все элементы <PLANET>, у которых значение атрибута REFERENCEтакое же, как значение атрибута UNITSэлемента PLANETSв корне документа; • PLANET/*выбирает все дочерние элементы элементов PLANET; • PLANET/*/DAY выбирает все элементы DAY — правнуки элементов PLANET, являющиеся детьми контекстного узла; • */*выбирает элементы-внуки текущего элемента; • astrophysics:PLANETвыбирает элемент PLANETв пространстве имен « astrophysics»; • astrophysics:*выбирает любые элементы в пространстве имен « astrophysics»; • PLANET[DAY and DENSITY]выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY>и один элемент <DENSITY>; • PLANET[(DAY or DENSITY) and MASS]выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY>или один элемент <DENSITY>, а также по крайней мере один элемент <MASS>; • PLANET[DAY and not(DISTANCE)]выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY>и нет элементов <DISTANCE>; • PLANET[MASS=/STANDARD/REFERENCE/MASS]выбирает все элементы <PLANET>, у которых значение элемента <MASS>равно значению элемента /<STANDARD>/<REFERENCE>/<MASS>. На этом мы завершаем в данный момент рассмотрение образцов выбора; связанный материал приводится в главе 7 при рассмотрении выражений XPath. Глава 5 начинается с изучения способов работы с данными в XML-документах путем сортировки и принятия решения на основе значений данных. |
|
||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх |
||||
|