|
||||
|
Глава 6Преобразование в XML, HTML, XHTML, RTF Предположим, web-узел вашей компании использует основанное на XML программное обеспечение фирмы Commerce One, в котором для безопасной коммуникации через Интернет применяется Java Message Service (JMS). Ваша деятельность была настолько успешной, что вы только что поглотили своего конкурента. К сожалению, для своего узла в Интернете ваш бывший конкурент использует другой основанный на XML продукт, RosettaNet. Как вам теперь преобразовать заказ на покупку xCBL Commerce One, написанный на XML, в заказ на покупку RosettaNet, также написанный на XML, но совершенно на другом диалекте? Разумеется, применить XSLT. Такого рода XML-XML преобразования становятся все более и более распространенными. Все больше компаний применяют JMS для безопасных коммуникаций через Интернет, и поскольку JMS выполняется в Java, будет разумным связать JMS с основанными на Java процессорами XSLT, такими, как Xalan или Saxon. В этой книге мы уже рассматривали преобразование XML в HTML, XML и простой текст, но в текущей главе сделаем это более подробно. Мы также рассмотрим здесь новый тип преобразования — из XML в JavaScript. В главе 10 мы познакомимся с преобразованиями из XML в базу данных на основе SQL, а в главе 11 — с преобразованиями из XML в XSL-FO. Основная задача XSLT состоит не просто в замене одного элемента на другой, но в полной реорганизации содержимого XML-документа. Например, вам может потребоваться реорганизовать planets.xmlв терминах плотности планет при помощи XSLT для создания нового XML-документа: <?xml version="1.0" encoding="UTF-8"?> <DATA> <DENSITY> <VALUE>.983</VALUE> <NAME>Mercury</NAME> <MASS>.0553</MASS> <DAY>58.65</DAY> <RADIUS>1516</RADIUS> </DENSITY> <DENSITY> <VALUE>.943</VALUE> <NAME>Venus</NAME> <MASS>.815</MASS> <DAY>116.75</DAY> <RADIUS>3716</RADIUS> </DENSITY> <DENSITY> <VALUE>1</VALUE> <NAME>Earth</NAME> <MASS>1</MASS> <DAY>1</DAY> <RADIUS>2107</RADIUS> </DENSITY> </DATA> Мы рассмотрим преобразование, которое полностью меняет содержимое planets.xml, оставляя только небольшой код HTML и код JavaScript для отображения нескольких кнопок в браузере. До сих пор мы создавали новые элементы только при помощи элементов буквального результата, то есть рассматривая новые элементы как текст и встраивая их в таблицу стилей. Но, как мы увидим в этой главе, не всегда возможно знать имена создаваемых новых элементов. Можно состыковать вместе создаваемые элементы по ходу дела, рассматривая их как сырой текст, но это явная недоработка, поскольку разметка трактуется как текст. В этой главе мы начнем применять элементы XSLT <xsl:element>, <xsl:attribute>, <xsl:processing-instruction>и <xsl:comment>для создания новых элементов, атрибутов, инструкций обработки и комментариев на этапе выполнения. Хорошее знание этих элементов необходимо при реорганизации содержимого XML. Мы также рассмотрим использование режимов XSLT для осуществления нескольких преобразований с документом и сориентируемся, как применять только один из нескольких подходящих шаблонов. В большей части этой главы исследуются возможности элемента <xsl:output>, с краткого обзора которого я и начну. Элемент <xsl:output>С элементом <xsl:output>мы впервые познакомились в главе 2 и использовали его, главным образом, для задания типа результирующего документа. Этот тип может задать, например, будет ли процессор XSLT записывать инструкцию обработки XML, <?xml version="1.0"?>, в начале документа, а также задать тип MIME (такой, как « text/xml» или « text/html») документов, отправляемых процессором XSLT из web-сервера браузеру. Кроме того, если вы установите выходной тип в HTML, большинство процессоров XSLT смогут распознать, что не всем элементам HTML необходимы закрывающие или открывающие теги и т.п. В следующем списке перечислены атрибуты <xsl:output>: • cdata-section-elements(необязательный). Задает имена тех элементов, чье содержимое должно выводиться как разделы CDATA. Принимает значения списка QName, разделенного символами-разделителями; • doctype-public(необязательный). Задает открытый идентификатор, который будет использован в объявлении <!DOCTYPE>в выходных данных. Устанавливается в строковое значение; • doctype-system(необязательный). Задает системный идентификатор, который будет использован в объявлении <!DOCTYPE>в выходных данных. Устанавливается в строковое значение; • encoding(необязательный). Задает кодировку символов. Устанавливается в строковое значение; • indent(необязательный). Определяет, будет ли выходной документ выровнен с отражением структуры вложенности. Устанавливается в yesили no; • media-type(необязательный). Задает тип MIME вывода. Устанавливается в строковое значение; • method(необязательный). Задает формат вывода. Принимает значения « xml», « html», « text» или допустимое имя QName; • omit-xml-declaration(необязательный). Определяет, будет ли включено в вывод объявление XML. Устанавливается в « yes» или « no»; • standalone(необязательный). Определяет, будет ли включено в вывод отдельное объявление XML, и если да — устанавливает его значение. Устанавливается в yesили no; • version(необязательный). Задает версию вывода. Принимает значение допустимого NMToken. Чаще, всего используется атрибут method, поскольку именно он определяет требуемый тип выходного дерева. Официально методом вывода до умолчанию является HTML, при условии, что выполняются все три следующих условия: • корневой узел результирующего дерева имеет дочерний элемент; • в названии элемента документа результирующего дерева присутствует часть « html» (в любой комбинации верхнего и нижнего регистров) и пустой URI пространства имен; • все текстовые узлы перед первым дочерним элементом корневого узла содержат только символы-разделители. Если выполняются все три этих условия, то по умолчанию метод вывода устанавливается в HTML. В ином случае методов вывода по умолчанию является XML. Тем не менее, не стоит полагаться на установки метода вывода по умолчанию, лучше явно присвоить этому атрибуту значение. Три распространенных значения атрибута method— « html», « xml» и « text», и мы познакомимся с ними в следующих разделах. Метод вывода: HTMLДля метода вывода HTML процессор XSLT должен предпринять определенные действия. Например, для этого метода атрибут versionопределяет версию HTML. Значение по умолчанию — 4.0. Этот метод не должен добавлять завершающий тег для пустых элементов. (Для HTML 4.0 пустыми элементами являются <AREA>, <BASE>, <BASEFONT>, <BR>, <COL>, <FRAME>, <HR>, <IMG>, <INPUT>, <ISINDEX>, <LINK>, <META>и <PARAM>.) Метод вывода HTML должен распознавать названия элементов HTML независимо от регистра. В соответствии с W3C, метод вывода HTML не должен скрывать содержимое элементов <SCRIPT>или <STYLE>. Например, следующий элемент буквального результата: <SCRIPT> if (х < у) {...} </SCRIPT> или следующий, использующий раздел CDATA: <SCRIPT> <![CDATA[if (х < у) {...}]]> </SCRIPT> должен быть преобразован в: <SCRIPT> if (х < у) {...} </SCRIPT> Метод вывода HTML не должен также подавлять символы <, встречающиеся в значениях атрибутов. При установке метода вывода в HTML процессор может учесть атрибут выравнивания. Если этот атрибут установлен в yes, процессор XSLT может добавить (или удалить) символы-разделители для выравнивания результирующего документа, поскольку это не влияет на отображение документа в браузере. Для метода вывода HTML значением по умолчанию является «yes». Как вы могли предположить, метод вывода HTML завершает инструкции обработки при помощи >, а не ?>, а также поддерживает отдельные атрибуты, как и HTML. Например, тег <TD NOWRAP="NOWRAP"> будет преобразован в: <TD NOWRAP> Для этого метода можно установить атрибут media-type, значением по умолчанию для которого является « text/html». Метод HTML не должен убирать символ &, который появляется в значении атрибута, если сразу за ним следует фигурная скобка. Атрибут encodingзадает используемую кодировку. Если присутствует элемент <HEAD>, этот метод вывода должен добавить элемент <МЕТА>сразу же после тега <HEAD>, определяя кодировку символов: <HEAD> <МЕТА http-equiv="Content-Type" content="text/html; charset=utf-8"> . . . При помощи атрибутов doctype-publicили doctype-systemможно вывести объявление типа документа непосредственно перед первым элементом, как мы увидим при преобразовании XML в XHTML. Таковы правила вывода HTML. Ниже приведен пример преобразования из XML в HTML с небольшими отклонениями. В этом случае таблица стилей будет фактически генерировать код JavaScript, демонстрируя создание JavaScript при помощи XSLT. В частности, мы прочитаем planets.xmlи создадим новый документ HTML, отображающий три кнопки — по одной для каждой из трех планет в planets.xml. При щелчке на кнопке на странице будет выведена масса соответствующей планеты. Все, что нам понадобится (листинг 6.1), — это два элемента <xsl:for-each>: один для прохода в цикле по трем планетам и создания для каждой кнопки HTML; и один для прохода по планетам и создания для каждой функции JavaScript. В качестве имен функций JavaScript я воспользуюсь названием планет; при вызове функция выведет массу соответствующей планеты. Заметьте, что для создания нужного кода JavaScript нужно всего лишь применить элемент <xsl:value-of>для получения названий и масс планет. Я также применю два новых элемента XSLT, <xsl:element>и <xsl:attribute-set>, которые мы рассмотрим позже в этой главе, для создания нового элемента и задания для него набора атрибутов. Листинг 6.1. Преобразование в JavaScript <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Mass Page </TITLE> <SCRIPT LANGUAGE='javascript'> <xsl:for-each select="PLANET"> <xsl:text> function </xsl:text><xsl:value-of select="NAME"/><xsl:text>() { display.innerHTML = 'The mass of </xsl:text> <xsl:value-of select="NAME"/> <xsl:text> equals </xsl:text> <xsl:value-of select="MASS"/> <xsl:text> Earth masses.'</xsl:text> } </xsl:for-each> </SCRIPT> </HEAD> <BODY> <CENTER> <H1>The Mass Page</H1> </CENTER> <xsl:for-each select="PLANET"> <P/> <xsl:element name="input" use-attribute-sets="attribs"/> </xsl:for-each> <P/> <P/> <DIV ID='display'></DIV> </BODY> </HTML> </xsl:template> <xsl:attribute-set name="attribs"> <xsl:attribute name="type">BUTTON</xsl:attribute> <xsl:attribute name="value"><xsl:value-of select="NAME"/></xsl:attribute> <xsl:attribute name="onclick><xsl:value-of select="NAME"/>()</xsl:attribute> </xsl:attribute-set> </xsl:stylesheet> Результат, включая элемент <SCRIPT>для нового кода JavaScript (листинг 6.2). Листинг 6.2. Результирующий документ с JavaScript <HTML> <HEAD> <TITLE> The Mass Page </TITLE> <SCRIPT LANGUAGE="javascript"> function Mercury() { display.innerHTML =
} function Venus() { display.innerHTML = 'The mass of Venus equals .815 Earth masses.' } function Earth() { display.innerHTML = 'The mass of Earth equals 1 Earth masses.' } </SCRIPT> </HEAD> <BODY> <CENTER> <H1>The Mass Page</H1> </CENTER> <P></P> <input type="BUTTON" value="Mercury" onclick="Mercury()"> <P></P> <input type="BUTTON" value="Venus" onclick="Venus()"> <P></P> <input type="BUTTON" value="Earth" onclick="Earth()"> <P></P> <P></P> <DIV ID="display"></DIV> </BODY> </HTML> Как видите, при помощи XSLT я написал код JavaScript для прохода в цикле по планетам. Этот документ HTML показан на рис. 6.1. При щелчке на кнопку выводится масса соответствующей планеты. Рис. 6.1. Преобразование XML в HTML при помощи JavaScript Метод вывода: XMLФормально при использовании метода вывода XML процессор XSLT создает хорошо сформированный внешний объект XML, который можно разобрать на общих условиях. Если корневой узел результирующего дерева имеет единственный дочерний узел-элемент и не имеет текстовых дочерних узлов. Тогда объект будет также хорошо сформированным документом XML. При использовании метода вывода XML атрибут versionустанавливает версию XML результата. Заметьте, что если процессор XSLT не поддерживает эту версию XML, он будет использовать ту версию XML, которую поддерживает. По умолчанию установлено значение 1.0. Атрибут encodingустанавливает кодировку для результирующего документа. Процессоры XSLT должны поддерживать, по крайней мере, значения «UTF-8» и «UTF-16». Если процессор XSLT работает с другими значениями и не поддерживает указанную кодировку, он может сгенерировать ошибку. Если он этого не сделает, процессор должен использовать вместо нее UTF-8 иди UTF-16. Процессор XSLT не должен использовать кодировку, которая не была принята консорциумом W3C (см. www.ww3.org/TR/REC-xml). Если никакой атрибут кодировки не указан, по умолчанию процессор XSLT должен выбрать «UTF-8» или «UTF-16».
Как и в случае с методом вывода HTML, если атрибут indentустановлен в «yes», метод вывода XML может добавить или удалить символы-разделители в результирующее дерево для того, чтобы выровнять результат. Значение по умолчанию — no. Заметьте, что если символы-разделители отбрасываются, информационное множество результирующего XML-документа должно быть таким же, как если бы символы-разделители вообще не добавлялись и не удалялись для выравнивания документа.
При помощи атрибута cdata-section-elementsможно задать разделенный символами-разделителями список имен элементов, чье содержимое должно трактоваться как разделы CDATA. Например, если установить атрибут cdata-section-elementsв « DATA»: <xsl:output cdata-section-elements="DATA"/> то следующий элемент буквального результата: <DATA><:DOCUMENT></DATA> будет преобразован в: <DATA><![CDATA<[DOCUMENT>]]></DATA> Кроме того, метод вывода XML будет выводить в результирующий документ объявление XML, если только атрибут omit-xml-declarationне будет установлен в yes. Как правило, объявление XML, помещаемое в результирующий документ, обычно включает версию XML (что обязательно) и информацию о кодировке (хотя формально информация о кодировке в документах XML не обязательна). Если задан атрибут standalone, результирующий документ должен включать объявление отдельного документа с тем же значением, что и значение у атрибута standalone. При использовании атрибута doctype-systemпроцессор создает объявление типа документа непосредственно перед первым элементом. В этом случае имя, следующее за <!DOCTYPE>, будет именем корневого элемента. Заметьте, что если вы также используете атрибут doctype-public, процессор XSLT выведет « PUBLIC», вслед за ним открытый идентификатор и затем системный идентификатор. Если вы не используете атрибут doctype-public, процессор выведет « SYSTEM» и вслед за ним системный идентификатор. Теоретически атрибут doctype-publicдолжен игнорироваться, если не задан также атрибут doctype-system, хотя большинство процессоров, кажется, не следуют этому правилу. Мы увидим, как работать с атрибутами doctype-publicи doctype-systemв этой главе при преобразовании XML в XHTML. Наконец, для метода вывода XML значением по умолчанию для атрибута media-typeявляется « text/xml». Вы уже встречали в этой книге многие XML-XML преобразования. Например, преобразование из главы 4 просто копировало один документ XML в другой. Обратите внимание на метод вывода, который установлен в XML: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Этот пример был приведен в начале главы, в нем planets.xmlреорганизован на основе плотности планет: <?xml version="1.0" encoding="UTF-8"?> <DATA> <DENSITY> <VALUE>.983</VALUE> <NAME>Mercury</NAME> <MASS>.0553</MASS> <DAY>58.65</DAY> <RADIUS>1516</RADIUS> </DENSITY> <DENSITY> <VALUE>.943</VALUE> <NAME>Venus</NAME> <MASS>.815</MASS> <DAY>116.75</DAY> <RADIUS>3716</RADIUS> </DENSITY> <DENSITY> <VALUE>1</VALUE> <NAME>Earth</NAME> <MASS>1</MASS> <DAY>1</DAY> <RADIUS>2107</RADIUS> </DENSITY> </DATA> Вот таблица стилей (листинг 6.3), создающая это преобразование. Листинг 6.3. Реорганизация planets.xml на основе плотности<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="PLANETS"> <DATA> <xsl:apply-templates/> </DATA> </xsl:template> <xsl:template match="PLANET"> <DENSITY> <VALUE> <xsl:value-of select="DENSITY"/> </VALUE> <xsl:apply-templates/> </DENSITY> </xsl:template> <xsl:template match="NAME"> <NAME> <xsl:value-of select="."/> </NAME> </xsl:template> <xsl:template match="MASS"> <MASS> <xsl:value-of select="."/> </MASS> </xsl:template> <xsl:template match="RADIUS"> <RADIUS> <xsl:value-of select="."/> </RADIUS> </xsl:template> <xsl:template match="DAY"> <DAY> <xsl:value-of select="."/> </DAY> </xsl:template> <xsl:template match="DENSITY"> </xsl:template> <xsl:template match="DISTANCE"> </xsl:template> </xsl:stylesheet> Следующий пример впервые был приведен в главе 5. В этом случае я только перечислил планеты из planets.xml, но в выходном документе я хотел видеть не просто фразу «The first three planets are: Mercury Venus Earth» (первые три планеты: Меркурий Венера Земля), a «The first three planets are: Mercury, Venus, and Earth.». Для этого я применил элементы <xsl:if>: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: <xsl:apply-templates select="PLANET"/> </PLANETS> </DOCUMENT> </xsl:template> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> <xsl:if test="position()!=last()">. </xsl:if> <xsl:if test="position()=last()-1">and </xsl:if> <xsl:if test="position()=last()">.</xsl:if> </xsl:template> </xsl:stylesheet> И вот результат: <?xml version="1.0" encoding="UTF-8"?> <DOCUMENT> <TITLE> The Planets </TITLE> <PLANETS> The first three planets are: Mercury, Venus, and Earth. </PLANETS> </DOCUMENT> Хотя многие книги рассматривают главным образом преобразования из XML в HTML, важно понять, что преобразования XML-XML завоевывают все большую популярность, поэтому на них я также останавливаю ваше внимание. Метод вывода: текстЭтот метод вывода представляет простой текст. В данном случае выходной документ являет собой только простой текст дерева документа. То есть процессор XSLT создает результирующее дерево, выводя строковое значение каждого текстового узла, без каких-либо сокращений. Значением по умолчанию для атрибута media-typeявляется « text/plain». Атрибут encodingустанавливает кодировку, используемую процессором XSLT для преобразования последовательностей символов в последовательности байтов. Заметьте, что если результирующий документ содержит символ, который не может быть представлен в выходной кодировке, процессор XSLT должен сгенерировать ошибку. В листинге 6.4 planets.xmlпреобразуется в простой текст при помощи метода текстового вывода. Листинг 6.4. Преобразование в простой текст <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" indent="yes"/> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> <xsl:text>'s mass is </xsl:text> <xsl:value-of select="MASS"/> <xsl:text> Earth masses. Its radius is </xsl:text> <xsl:value-of select="RADIUS"/> <xsl:text> miles. Its day is </xsl:text> <xsl:value-of select="DAY"/> <xsl:text> Earth days long.</xsl:text> </xsl:template> </xsl:stylesheet> И вот результат — просто чистый текст, никакой разметки, никаких пропущенных символов, никаких инструкций обработки: Mercury's mass is .0553 Earth masses. Its radius is 1516 miles. Its day is 58.65 Earth days long. Venus's mass is .815 Earth masses. Its radius is 3716 miles. Its day is 116.75 Earth days long. Earth's mass is 1 Earth masses. Its radius is 2107 miles. Its day is 1 Earth days long. С другой стороны, метод текстового вывода предназначен не только для создания простого текста, он также применяется для любых текстовых форматов, отличных от XML и HTML. Как мы видели в главе 2, с его помощью можно создавать форматированные документы в формате Rich Text Format (RTF). Rich Text Format использует встроенные текстовые коды, задающие формат документов, и при помощи метода текстового вывода вы можете самостоятельно поместить эти коды в документы. Следующий пример таблицы стилей первоначально был приведен в главе 2, где она использовалась с целью перевода planets.xml в формат RTF, для понимания которого вы теперь лучше вооружены. В этом случае я преобразую planets.xmlв planets.rtf, используя коды RTF как элементы буквального результата: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="/PLANETS">{\rtf1\ansi\deff0{\fonttbl {\\fcharset0 Courier New:}} \viewkind4\ucl\pard\lang1033\b\ The Planets Table\par \b0 Name\tab Mass\tab Rad.\tab Day\par <xsl:apply-templates/> \par }</xsl:template> <xsl:template match="PLANET"> <xsl:value-of select="NAME"/> \tab <xsl:value-of select="MASS"/> \tab <xsl:value-of select="RADIUS"/> \tab <xsl:value-of select="DAY"/> \tab \par </xsl:template> </xsl:stylesheet> Результирующий документ RTF, planets.rtf, показан на рис. 6.2 в редакторе Microsoft Word 2000. Рис. 6.2. Файл planets.rtf в Microsoft Word Заметьте, что методом вывода является текст, а не что-нибудь наподобие « rtf»: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="/PLANETS">{\rtf1\ansi\deff0{\fonttbl {\\fcharset0 Courier New:}} \viewkind4\ucl\pard\lang1033\b\ The Planets Table\par . . . Отметьте также, что я поместил коды RTF сразу же после элемента <xsl:template>, поскольку документы RTF должны с самого начала начинаться с кодов RTF; если бы я начал вставлять коды RTF на следующей строке, как, например: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="/PLANETS">{ \rtf1\ansi\deff0{\fonttbl {\\fcharset0 Courier New:}} \viewkind4\ucl\pard\lang1033\b\ The Planets Table\par . . . то выходной файл RTF начинался бы с символа новой строки, что привело бы к ошибке в приложении, работающем с файлом RTF (например, вероятно, Microsoft Word). Вывод в формате XHTMLW3C представил XHTML как последователя HTML, но ни в XSLT 1.0, ни в рабочем проекте XSLT 1.1 нет никакой специальной поддержки для преобразований из XML в XHTML. Предполагается, что надлежащая поддержка будет включена в XSLT 2.0. Тем не менее, при помощи процессоров XSLT все равно можно создавать документы XHTML.
Помимо заботы о том, что ваш документ должен удовлетворять правилам XHTML (таким, как отсутствие отдельных атрибутов, заключение в кавычки всех значений атрибутов, использование для разметки символов в нижнем регистре, наличие для каждого открывающего тега закрывающего, проверка того, что документ является хорошо сформированным XML, и т.д.), основная задача — гарантировать, что в результирующем документе появится элемент <!DOCTYPE>. Ниже приведены элементы <!DOCTYPE>, которые следует использовать с тремя типами XHTML 1.0 — строгим (strict), переходным (transitional) и кадровым (frameset): <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> А вот элемент <!DOCTYPE>для XHTML 1.1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
При установке метода вывода в XML для создания элемента <!DOCTYPE>можно воспользоваться атрибутами doctype-systemи doctype-publicэлемента <xsl:output>. Вот пример элемента <xsl:output>, создающего элемент <!DOCTYPE>для переходного XHTML 1.0: <xsl:output method="xml" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" indent="yes"/> В листинге 6.5 приведена полная таблица стилей planets.html, использующая этот элемент <xsl:output>для преобразования planets.xmlв допустимый документ XHTML. Листинг 6.5. Преобразование planets.xml в XHTML <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" indent="yes"/> <xsl:template match="/PLANETS"> <html> <head> <title> The Planets Table </title> </head> <body> <h1> The Planets Table </h1> <table> <tr> <td>Name</td> <td>Mass</td> <td>Radius</td> <td>Day</td> </tr> <xsl:apply-templates/> </table> </body> </html> </xsl:template> <xsl:template match="PLANET"> <tr> <td><xsl:value-of select="NAME"/></td> <td><xsl:apply-templates select="MASS"/></td> <td><xsl:apply-templates select="RADIUS"/></td> <td><xsl:apply-templates select="DAY"/></td> </tr> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <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> Вот результирующий файл XHTML: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title> The Planets Table </title> </head> <body> <h1> The Planets Table </h1> <table> <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> <tr> <td>Venus</td> <td>.815 (Earth = 1)</td> <td>3716 miles</td> <td>116.75 days</td> </tr> <tr> <td>Earth</td> <td>1 (Earth = 1)</td> <td>2107 miles</td> <td>1 days</td> </tr> </table> </body> </html> Полученный документ, planets.html, является и хорошо сформированным, и допустимым документом XHTML 1.0 — в соответствии с программой допустимости W3C для HTML и XHTML, расположенной по адресу: http://validator.w3.org/file-upload.html. Заметьте, что, поскольку документы XHTML являются в то же время хорошо сформированными XML-документами, мы воспользовались методом вывода XML, — поэтому преобразование не было слишком сложным; единственной сложностью оказалось создание элемента <!DOCTYPE>. Изменение структуры документа на основе входных данныхДо сих пор созданные мной шаблоны основывались на определенном жестком скелете, точно задающем, что должно попасть в выходной документ и в каком порядке. Но при помощи таких элементов XSLT, как <xsl:element>, <xsl:attribute>, <xsl:text>и т.д., можно создавать новые узлы в рабочем порядке, на основе данных входного документа. У вас уже есть небольшой опыт в этой области, поскольку мы работали с шаблонами значений атрибутов в главе 3. Как вы помните, при помощи таких шаблонов можно присвоить атрибуту значение выражения XPath, если заключить это выражение в фигурные скобки, { и }. Например, чтобы установить атрибут NAMEв строковое значение элемента <DESCRIPTION>, дочернего элемента контекстного узла, это значение можно присвоить так: NAME={DESCRIPTION}. Теперь мы можем более подробно рассмотреть всю тему создания новых элементов и атрибутов «с ходу», начав с <xsl:element>. Элемент <xsl:element>: создание новых элементов на этапе выполненияНовые элементы можно создавать при помощи элемента <xsl:element>, который очень удобен для задания имени нового элемента на этапе выполнения. У этого элемента три атрибута: • name(обязательный). Имя создаваемого элемента. Принимает значение шаблона значений атрибута, возвращающего QName; • namespace(необязательный). URI пространства имен нового элемента. Принимает значение шаблона значений атрибута, возвращающего URI; • use-attribute-sets(необязательный). Задает наборы атрибутов, содержащие атрибуты этого элемента. Принимает значение списка элементов QName, разделенных символами-разделителями. Элемент <xsl:element>содержит тело шаблона. Пусть, например, мне нужно хранить названия планет в атрибутах NAME, а не в элементе <NAME>в planets.xml: <?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET NAME="Mercury"> <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> . . . Предположим теперь, что при помощи значений этого атрибута мне нужно создать имена новых элементов в результирующем документе — такие, как <Mercury>, <Venus>и <Earth>: <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <Mercury> <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><!--В перигелии--> </Mercury> . . . В этом случае я не знаю имени выходного элемента до времени выполнения, потому и не могу просто применить элемент буквального результата. Я мог бы скомпоновать новый элемент, трактуя его как текст (что и показано в примере ниже, где я вывожу символы, подобные «<», при помощи атрибута disable-output-escapingэлемента <xsl:text>): <?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="PLANET"> <xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="@NAME"/> <xsl:text disable-output-escaping="yes">></xsl:text> <xsl:apply-templates/> <xsl:text disable-output-escaping="yes"></</xsl:text> <xsl:value-of select="@NAME"/> <xsl:text disable-output-escaping="yes">></xsl:text> </xsl:template> </xsl:stylesheet> Но это грубый способ, при котором разметка рассматривается как простой текст. С другой стороны, зная название планеты, я могу создать новый элемент при помощи <xsl:element> (листинг 6.6), получив название новой планеты из атрибута NAMEследующим образом. Листинг 6.6. Применение <xsl:element> <?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="PLANET"> <xsl:element name="{@NAME}"> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet> Этот способ намного чище и проще. Ниже показан результат, в котором на этапе выполнения созданы новые элементы с именами различных планет: <?xml version="1.0" encoding="UTF-8"/> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <Mercury> <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 перигелии--> </Mercury> <Venus> <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 перигелии--> </Venus> <Earth> <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 перигелии--> </Earth> </PLANETS> Таким способом можно создавать новые элементы и задавать им имя во время преобразования XSLT. Элемент <xsl:attribute>: создание новых атрибутовАналогично тому, как вы можете создавать новые элементы при помощи <xsl:element>и устанавливать имя и содержимое элемента на этапе выполнения, при помощи элемента <xsl:attribute>это можно делать для атрибутов. У элемента два атрибута: • name(обязательный). Имя нового атрибута. Принимает значение шаблона значений атрибута, возвращающего QName; • namespace(необязательный). Пространство имен нового атрибута. Устанавливается в URI. Этот элемент содержит в себе тело шаблона, которое устанавливает значение атрибута. В листинге 6.7 я создаю новые элементы <PLANET>с атрибутами, которые соответствуют различным названиям планет (значения берутся из атрибута COLORисходных элементов <PLANET>). Листинг 6.7. Применение <xsl:attribute> <?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <PLANET> <xsl:attribute name="{NAME}"> <xsl:value-of select="@COLOR"/> </xsl:attribute> </PLANET> </xsl:template> </xsl:stylesheet> Как можно видеть в приведенном ниже результате, я создал новые атрибуты «с ходу», используя названия планет: <НТМL> <HEAD> <TITLE> Planets </TITLE> </HEAD> <BODY> <PLANET Mercury="RED"> </PLANET> <PLANET Venus="WHITE"> </PLANET> <PLANET Earth="BLUE"> </PLANET> </BODY> </HTML> Элемент <xsl:comment>: создание комментариевПо ходу дела можно также создавать и комментарии при помощи элемента <xsl:comment>. Этот элемент не имеет атрибутов и содержит тело шаблона, задающего текст комментария. В листинге 6.8 я создаю комментарии для замены элементов <PLANET>; текст комментария включает название планеты. Листинг 6.8. Применение <xsl:comment> <?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:comment>This was the <xsl:value-of select="NAME"/> element</xsl:comment> </xsl:template> </xsl:stylesheet> Вот результат: <HTML> <HEAD> <TITLE> Planets </TITLE> </HEAD> <BODY> <!--This was the Mercury element--> <!--This was the Venus element--> <!--This was the Earth element--> </BODY> </HTML> Элемент <xsl:processing-instruction>: создание инструкций обработкиПри помощи элемента <xsl:processing-instruction>можно создавать новые инструкции обработки. У этого элемента один атрибут: • name(обязательный). Задает имя инструкции обработки. Принимает значение шаблона значений атрибута, возвращающего NCName. В следующем примере я удалил инструкцию <?xml-stylesheet?>из начала planets.xml: <?xml version="1.0"?> <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> . . . Для того чтобы снова добавить эту инструкцию обработки, можно применить элемент <xsl:processing-instruction>(листинг 6.9). Пункты typeи hrefв инструкции обработки, например <?xml-stylesheettype="text/xml" href="planets.xsl"?>, в действительности не являются атрибутами, поэтому их значения нужно устанавливать не при помощи <xsl:attribute>, а используя простой текст. Листинг 6.9. Применение <xsl:processing-instruction> <?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:processing-instruction name="xml-stylesheet"> <xsl:text>type="text/xml" href="planets.xsl"</xsl:text> </xsl:processing-instruction> <xsl:apply-templates/> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Вот результат, где инструкция обработки <?xml-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><!--At perihelion--> </PLANET> . . . Элемент <xsl:document>: создание нескольких выходных документовВ рабочем проекте XSLT 1.1 был представлен новым элемент, <xsl:document>, предназначенный для поддержки нескольких выходных документов, — и, скорее всего, этот элемент будет добавлен в XSLT 2.0. Он имеет следующие атрибуты: • href(обязательный). Указывает место, в которое должен быть помещен новый документ. Устанавливается в абсолютный или относительный URI, без идентификатора фрагмента; • method(необязательный). Устанавливает метод вывода, используемый для создания результирующего документа. Устанавливается в «xml», «html», «text» или QName, которое не является NCName; • version(необязательный). Задает версию выходного документа. Устанавливается в NMTOKEN; • encoding(необязательный). Задает кодировку выходного документа. Устанавливается в строку; • omit-xml-declaration(необязательный). Принимает значения «yes» или «no» для того, чтобы пропускать или не пропускать объявление XML; • cdata-section-elements(необязательный). Определяет имена тех элементов, чье содержимое вы хотите вывести как разделы CDATA. Принимает значение разделенного символами-разделителями списка QName; • doctype-public(необязательный). Определяет открытый идентификатор, который будет использован в объявлении <!DOCTYPE>вывода. Устанавливается в строковое значение; • doctype-system(необязательный). Определяет системный идентификатор, который будет использован в объявлении <!DOCTYPE>вывода. Устанавливается в строковое значение; • encoding(необязательный). Задает кодировку символов. Устанавливается в строковое значение; • indent(необязательный). Определяет выравнивание вывода для отображения структуры вложенности. Устанавливается в «yes» или «no»; • media-type(необязательный). Задает тип MIME вывода. Устанавливается в строковое значение; • standalone(необязательный). Определяет, нужно ли включать в вывод отдельное объявление, и если да, задает его значение. Устанавливается в «yes» или «no». Этот элемент содержит тело шаблона. В следующем примере, основанном на упрощенной таблице стилей, я создаю в документе HTML две рамки (frame), и два HTML-документа, которые будут в них отображаться, frame1.htmlи frame2.html. Первую рамку и документ, который в ней появится, frame1.html, я создам при помощи <xsl:document>следующим образом (заметьте, что здесь я устанавливаю атрибут version в «1.1», поскольку мы используем возможность, входящую только в рабочий проект XSLT 1.1, но «1.1», вероятно, не будет правильным значением атрибута versionв долгосрочном периоде; если элемент <xsl:document>будет включен в XSLT 2.0, версию следует установить в «2.0»): <HTML xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.1"> <HEAD> <TITLE> Two Frames </TITLE> </HEAD> <FRAMESET cols="50%, 50%> <FRAME src="frame1.html"/> <xsl:document href="frame1.html"> <HTML> <HEAD> <TITLE> Frame 1 </TITLE> </HEAD> <BODY> <H1>This is frame 1.</H1> </BODY> </HTML> </xsl:document> . . . После этого я могу создать вторую рамку и документ для вывода в нее, frame2.html(листинг 6.10). Листинг 6.10. Применение <xsl:document> <HTML xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.1"> <HEAD> <TITLE> Two Frames </TITLE> </HEAD> <FRAMESET cols="50%, 50%> <FRAME src="frame1.html"/> <xsl:document href="frame1.html"> <HTML> <HEAD> <TITLE> Frame 1 </TITLE> </HEAD> <BODY> <H1>This is frame 1.</H1> </BODY> </HTML> </xsl:document> <FRAME src="frame2.html"/> <xsl:document href="frame2.html"> <HTML> <HEAD> <TITLE> Frame 2 </TITLE> </HEAD> <BODY> <H1>This is frame 2.</H1> </BODY> </HTML> </xsl:document> </FRAMESET> </HTML>
Элемент <xsl:namespace>: создание объявлений пространств именВ XSLT 2.0 включен еще один новый элемент: <xsl:namespace>, позволяющий добавлять в результирующий документ объявления пространств имен. Однако на текущий момент больше об этом элементе ничего не известно, так что я не могу дать здесь более подробную информацию. Следите за web-узлом W3C. Элемент <xsl:attribute-set>: создание наборов атрибутовИногда при создании нового элемента возникает потребность добавить в него сразу несколько атрибутов. Существует простой способ сделать это при помощи элемента <xsl:attribute-set>. У этого элемента два атрибута: • name(обязательный). Имя набора атрибутов. Устанавливается в QName; • use-attribute-sets(необязательный). Имена других наборов атрибутов, которые вы хотите включить в этот набор. Устанавливается в список QName, разделенных символами-разделителями. Элемент <xsl:attribute-set>содержит элементы <xsl:attribute>, по одному для каждого нового создаваемого атрибута. При использовании <xsl:attribute-set>для создания нового набора атрибутов элемента нужно задать имя набору. Затем при создании нового элемента для использования набора атрибутов это имя можно присваивать атрибуту use-attribute-setsэлементов <xsl:copy>, <xsl:element>, <xsl:for-each>и даже самого элемента <xsl:attribute-set>. Ранее в текущей главе мы уже рассматривали один пример применения наборов атрибутов в JavaScript при создании шаблона. В том примере я при помощи набора атрибутов задавал все атрибуты HTML-кнопок планет и затем создавал их, используя этот набор атрибутов в элементе <xsl:element>: <BODY> <CENTER> <H1>The Mass Page</H1> </CENTER> <xsl:for-each select="PLANET"> <P/> <xsl:element name="input" use-attribute-sets="attribs"/> </xsl:for-each> <P/> <P/> <DIV ID='display'></DIV> </BODY> </HTML> </xsl:template> <xsl:attribute-set name="attribs"> <xsl:attribute name="type">BUTTON</xsl:attribute> <xsl:attribute name="value"><xsl:value-of select="NAME"/></xsl:attribute> <xsl:attribute name="onclick"><xsl:value-of select="NAME"/>()</xslt:attribute> </xsl:attribute-set> В результирующем документе такой набор атрибутов был добавлен каждой кнопке HTML: <Р></Р> <input type="BUTTON"value="Mercury" onclick="Mercury()"> <P></P> <input type="BUTTON" value="Venus" onclick="Venus()"> <P></P> <input type='BUTTON" value="Earth" onclick="Earth()"> В листинге 6.11 я воспользуюсь набором атрибутов для нумерации планет в planets.xml. Каждому элементу <PLANET>я добавил два атрибута: numberи total. В атрибуте numberбудет храниться номер планеты начиная с 1, а атрибут totalбудет содержать общее число планет в planets.xml, которое можно найти при помощи функции count(мы рассмотрим ее в главе 8). Листинг 6.11. Применение <xsl:attribute-set> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version=1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="*"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="PLANET"> <xsl:copy use-attribute-sets="numbering"> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:attribute-set name="numbering"> <xsl:attribute name="number"><xsl:number/></xsl:attribute> <xsl:attribute name="total"><xsl:value-of select="count(//PLANET)"/> </xsl:attribute> </xsl:attribute-set> </xsl:stylesheet> Обратите внимание на то, что в результирующем документе каждый элемент <PLANET>имеет атрибуты numberи total: <?xml version="1.0" encoding="UTF-8"?> <PLANETS> <PLANET number="1" total="3"> <NAME>Mercury</NAME> <MASS>.0553</MASS> <DAY>58.65</DAY> <RADIUS>1516</RADIUS> <DENSITY>.983</DENSITY> <DISTANCE>43.4</DISTANCE> </PLANET> <PLANET number="2" total="3"> <NAME>Venus</NAME> <MASS>.815</MASS> <DAY>116.75</DAY> <RADIUS>3716</ RADIUS> <DENSITY>.943</DENSITY> <DISTANCE>66.8</DISTANCE> </PLANET> <PLANET number="3" total="3"> <NAME>Earth</NAME> <MASS>1</MASS> <DAY>1</DAY> <RADIUS>2107</RADIUS> <DENSITY>1</DENSITY> <DISTANCE>128.4</DISTANCE> </PLANET> </PLANETS> Пропуск объявления XML и создание фрагментов XMLПри знакомстве с XSLT многих смущает объявление XML, которое всегда появляется наверху их результирующих документов, особенно если они не отдают себе отчёт в том, что методом вывода по умолчанию является XML. Разумеется, существует способ избавиться от объявления XML, что может быть полезно при создании хорошо сформированных фрагментов XML, не обязательно являющихся законченными документами. Достаточно только установить атрибут omit-xml-declarationэлемента <xsl:output> в «yes»: <xsl:output method="xml" omit-xml-declaration="yes"/>. В следующем примере я только уберу из документа объявление XML, копируя при этом все остальное. Заметьте, что объявление XML не есть узел, поэтому ничего страшного, если оно будет удовлетворять копирующему шаблону: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" omit-xml-declaration="yes"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Эта таблица стилей преобразует planets.xml: <?xml version="1.0"?> <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> . . . в новую версию без объявления XML: <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> . . . Полезно знать об этом при создании фрагментов XML или выполнении другой работы. Тем не менее, заметим, что во всех законченных документах XML — даже написанных самостоятельно при помощи разнообразных приложений XML, таких как WML — в начале обязательно должно быть объявление XML. Создание уникальных идентификаторов при помощи generate-idПри существенном изменении структуры документов важно рассмотреть еще одну тему: как в результирующих документах создавать идентификаторы элементов для обращения к ним при необходимости. Представьте, например, что вам нужно при помощи таблицы стилей добавить в документ оглавление, записи в котором будут представлять собой гиперссылки, так чтобы пользователь по щелчку на них сразу переходил к нужному разделу. В этом случае потребуется какой-то способ идентификации элементов в результирующем документе, в чем поможет функция generate-id. В следующем примере я добавляю составленное из гиперссылок оглавление в planets.html. Для создания оглавления я прохожу в цикле по всем планетам при помощи элемента <xsl:for-each>. На каждом шаге цикла я создаю гиперссылку и при помощи шаблона значений атрибута создаю атрибут HREF, который устанавливается в уникальный идентификатор для рассматриваемой планеты. Заметьте, что, несмотря на свое имя, функция generate-idсоздает только строковый идентификатор элемента, она не создает атрибуты ID: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <xsl:for-each select="PLANET"> <H2><A HREF="#{generate-id()}"> <xsl:value-of select="NAME"/> </A></H2> <P/> </xsl:for-each> . . . Эта таблица стилей (листинг 6.12) генерирует для каждой планеты идентификатор и создает требуемые гиперссылки. Функция generate-idне только создает для элемента новый идентификатор, но и возвращает его при последующем применении generate-idк этому элементу. В данном случае это удобно, поскольку таким образом я могу создать закладки гиперссылки в HTML-таблице данных планет, установить по очереди атрибут <NAME>закладки в идентификатор для каждого элемента <PLANET>— так, чтобы он стал гиперссылкой-назначением. Листинг 6.12. Применение функции generate-id <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <xsl:for-each select="PLANET"> <H2><A HREF="#{geherate-id()}" > <xsl:value-of select="NAME"/> </A></H2> <P/> </xsl:for-each> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><A NAME="{generate-id(.)}"> <xsl:value-of select="NAME"/> </A></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="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> Вот как это делается: сейчас я создал гиперссылки с атрибутом HREF, значение которого равно идентификатору элемента <PLANET>; при помощи этого же идентификатора я сделал каждый элемент <PLANET>назначением гиперссылки. Когда пользователь щелкает на гиперссылку в оглавлении, браузер прокручивает данные до соответствующей записи планеты в HTML-таблице. (Заметьте, что для того чтобы большинство браузеров осуществляли прокрутку, HTML-таблица должна быть вне пределов экрана.) Каждый процессор XSLT создает свои собственные идентификаторы; ниже приведен результат для процессора Xalan: <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <H2> <A href="#N5">Mercury</A> </H2> <Р></Р> <Н2> <A href="#N20">Venus</A> </H2> <P></P> <H2> <A href="#N3B">Earth</A> </H2> <Р></Р> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD><A NAME="N5">Mercury</A></TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> <TR> <TD><A NAME="N20">Venus</A></TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD><A NAME="N3B">Earth</A></TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD> </TR> </TABLE> </BODY> </HTML> Результат показан на рис. 6.3 (в том числе — оглавление из гиперссылок). Пользователю достаточно щелкнуть на гиперссылке к соответствующей записи в таблице. Рис. 6.3. Использование сгенерированных идентификаторов в гиперссылках Создание разделов CDATAПри преобразованиях XML-XML может потребоваться вывести разделы CDATA. В XSLT это легко осуществимо при помощи атрибута cdata-section-elementsэлемента <xsl:output>. Упомянутый атрибут позволяет указать, содержимое каких элементов следует заключить в раздел CDATA. Последнее может понадобиться, например, при создании элементов сценария, когда браузер требует заключения кода сценария в раздел CDATA. В следующем примере я поместил содержимое элементов <NAME>и <MASS>в planets.xmlв разделы CDATA: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" cdata-section-elements="NAME MASS"/> <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><![CDATA[Mercury]]></NAME> <MASS UNITS="(Earth = 1)"><![CDATA[.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><![CDATA[Venus]]></NAME> <MASS UNITS="(Earth = 1)"><![CDATA[.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> . . .
Установка кодировки символовДля значительных переработок исходных документов важно знать, что можно устанавливать кодировку символов в результирующих документах при помощи атрибута encoding элемента <xsl:output>. Однако нет гарантии, что ваш процессор XSLT будет поддерживать выбранную кодировку, поскольку процессоры обязаны поддерживать только кодировки UTF-8 и UTF-16. С другой стороны, если вы используете символ, не поддерживаемый в используемой кодировке, процессор XSLT либо выведет символ как ссылку на сущность, либо сгенерирует ошибку.
Атрибут encodingне обязателен в объявлении XML документа; если он отсутствует, разборщики XML по умолчанию принимают кодировку UTF-8. Если вы хотите явно задать кодировку или использовать другую, ее можно задать следующим образом — там, где я задаю кодировку UTF-16: <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="UTF-16"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> Вот что появится в выходном документе: <?xml version="1.0" encoding="UTF-16"?> <?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> . . . Режимы: форматирование в зависимости от контекстаВ некоторых случаях выбор узлов может требовать буквально мастерства. Например, шаблон с условием выбора «*» выбирает и «PLANET». Если вам нужно обрабатывать элементы <PLANET>по-другому, чем все остальные элементы, вы можете задать шаблон, выбирающий « PLANET»; тогда процессор XSLT определит, что « PLANET» ближе к элементу <PLANET>, чем «*», и воспользуется правилом выбора « PLANET». Но не всегда все так просто. Например, вам может быть нужно, чтобы правило « PLANETS» выбирало не все, а лишь некоторые элементы <PLANET>. Один из способов решения этой задачи — воспользоваться режимами (mode). Для задания режима обработки служит атрибут modeэлемента <xsl:apply-templates>. При задании режима будут применяться только те шаблоны, элементы <xsl:template>, атрибут modeкоторых установлен в тот же режим. Таким образом, при помощи шаблона можно выбрать только один определенный узел, даже если в ином случае они бы выбирали много узлов. Это очень удобно, если, например, нам нужно обработать какие-то элементы <PLANET>отлично от других. Рассмотрим пример. Здесь я создал новый режим обработки данных планет «fancy» (красивый), отображающий данные полужирным шрифтом. Но я хочу, чтобы полужирным выделялись только данные Земли, а не других планет. Это может составить проблему: поскольку все данные планет хранятся в одном типе элемента — <PLANET>, — постольку образец « PLANET» выберет их все. Режим решает это затруднение. В частности, если текущая планета — Земля, я могу установить режим обработки в « fancy»: <?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"> <xsl:if test="NAME='Earth'"> <TR> <TD><xsl:apply-templates select="NAME" mode="fancy"/></TD> <TD><xsl:apply-templates select="MASS" mode="fancy"/></TD> <TD><xsl:apply-templates select="RADIUS" mode="fancy"/></TD> <TD><xsl:apply-templates select="DAY" mode="fancy"/></TD> </TR> </xsl:if> <xsl:if test=NAME!='Earth'"> <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:if> </xsl:template> . . . После этого я устанавливаю нужные правила шаблона (листинг 6.13), оба для режима обработки « fancy», который выделяет выходные данные полужирным, и не задаю никакого специального режима обработки. Листинг 6.13. Применение режимов <?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"> <xsl:if test="NAME='Earth'"> <TR> <TD><xsl:apply-templates select="NAME" mode="fancy"/></TD> <TD><xsl:apply-templates select="MASS" mode="fancy"/></TD> <TD><xsl:apply-templates select="RADIUS" mode="fancy"/></TD> <TD><xsl:apply-templates select="DAY" mode="fancy"/></TD> </TR> </xsl:if> <xsl:if test=NAME!='Earth'"> <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:if> </xsl:template> <xsl:template match="NAME"> <xsl:value-of select="."/> </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:template match="NAME" mode="fancy"> <B><xsl:value-of select="."/></B> </xsl:template> <xsl:template match="MASS" mode="fancy"> <B> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </B> </xsl:template> <xsl:template match="RADIUS" mode="fancy"> <B> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </B> </xsl:template> <xsl:template match="DAY" mode="fancy"> <B> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </B> </xsl:template> </xsl:stylesheet> Вот результат. Заметьте, что одни только данные Земли выделены полужирным: <HTML> <HEAD> <TITLE> The Fancy Planets Table </TITLE> </HEAD> <BODY> <H1> The Fancy Planets Table </Н1> <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> <TR> <TD>Venus</TD> <TD>.815 (Earth = 1)</TD> <TD>3716 miles</TD> <TD>116.75 days</TD> </TR> <TR> <TD><B>Earth</B></TD> <TD><B>1 (Earth = 1)</B></TD> <TD><B>2107 miles</B></TD> <TD><B>1 days</B></TD> </TR> </TABLE> </BODY> </HTML> Этот документ показан на рис. 6.4. Рис. 6.4. Применение режимов обработки Режимы также очень удобны в том случае, когда требуется обработать один и тот же документ более одного раза; классический пример — создание оглавления. Следующий пример демонстрирует создание оглавления. Я добавил в planets.xmlоглавление в элементе <TOC>(table of contents, оглавление), имеющем три элемента <ENTRY>для каждой из планет. Заметьте, что здесь мне необходимо применить два шаблона, выбирающие элементы <PLANET>, — один для создания оглавления и еще один для копирования всех элементов <PLANET>в результирующий документ — я использую режимы для того, чтобы их различать. Начну с установки режима в « toc» и применения шаблона, который поддерживает этот режим и создает оглавление: <?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"> <PLANETS> <TOC> <xsl:apply-templates mode="toc"/> </TOC> . . . </PLANETS> </xsl:template> <xsl:template match="PLANET" mode="toc"> <ENTRY> <xsl:value-of select="NAME"/> </ENTRY> </xsl:template> . . . Затем я применяю общий шаблон без каких-либо режимов обработки ко всем элементам и атрибутам, копируя их в результирующий документ (листинг 6.14). Листинг 6.14. Создание оглавления<?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"> <PLANETS> <TOC> <xsl:apply-templates mode="toc"/> </TOC> <xsl:apply-templates select="@*|node()"/> </PLANETS> </xsl:template> <xsl:template match="PLANET" mode="toc"> <ENTRY> <xsl:value-of select="NAME"/> </ENTRY> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> И, наконец, результат, выводящий planets.xmlс оглавлением: <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <TOC> <ENTRY>Mercury</ENTRY> <ENTRY>Venus</ENTRY> <ENTRY>Earth</ENTRY> </TOC> <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><!--В перигелии-> </PLANET> . . . </PLANETS> В этой главе мы рассмотрели большую часть базового материала, и в следующей главе я собираюсь уделить внимание созданию полных выражений XPath. Хотя вы уже немного знакомы с этой темой по обсуждению образцов выбора в главе 4, нам осталось еще рассмотреть весьма значительную часть. |
|
||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх |
||||
|