Pull to refresh

Добавь Javascript в XSLT

Reading time 22 min
Views 3.2K
Здравствуйте уважаемые хабражители!
Мне хотелось бы представить вам свой маленький проект который как я думаю может быть полезен многим из вас. XSR это расширение для XSLT процессора Saxon, которое позволяет вам использовать JavaScript прямо в коде XSLT программы. Итак что у нас есть:
  • Saxon XSLT processor — резонный выбор для того кто хочет использовать современный XSLT 2, а насколько я знаю наиболее полная имплементация есть лишь в Saxon'e.
  • Mozilla Rhino JavaScript engine — по сути виртуальная JavaScript машина написанная на Java.
  • Apache ant build tool в основном используется для автоматизации рутинных операций (к примеру развертывание приложения на удаленном сервере, после автоматической компрессии всех файлов...
  • Желание соеденить это все вместе и получить работающий JavaScript в XSLT runtime
Итак приступим непосредственно к установке и использованию, дабы показать все на живых примерах. Для начала можете ознакомится с основными возможностями моей поделки на этой странице: Описание (англ.).

Кратко об установке и настройке

  • В случае если у вас не установлен ANT, то делаем это ознакомившись с описанием к примеру здесь
  • Забираем trunk отсюда. Там вы найдете проект под NetBeans, исходники, бинарники и тесты. Сразу оговорюсь, я не Java — программист а поэтому мой код со стороны тех из вас кто является профессионалом в данной области может показаться хмм… забавным?
  • Идем в папку test и проверяем правильность указания путей в файле build.xml
  • Идем в консоли в туже папку test, пишем ant и наблюдаем за тем как выполняются примеры из файла test.xsl / test.js
Для использования расширения вам необходимо будет добавить следующее определение пространства имен к корневому элементу XSLT документа:
xmlns:xsr="java:/xsr.XSRElementFactory"

Затем добавьте extension-element-prefixes="xsr" туда же — т.е. в список аттрибутов корневого элемента. Данная инструкция скажет Saxon'у о том что xsr префикс — определяет внешнее расширение. Другими словами после перечисленных манипуляций корневой элемент будет выглядеть примерно так:
<xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Использование инструкции xsr:script


Есть два способа определения и использования xsr:script.
Во — первых вы можете использовать href аттрибут для подключения (и незамедлительного исполнения) внешнего JavaScript файла. Вот так:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <!-- script defined in external file --><br>        <xsr:script href="test.js" /><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Данный код подключит внешний файл test.js (который должен быть расположен относительно местоположения документа) к вашему xsl — документу и выполнит его.

Во — вторых вы можете писать свой JavaScript код напрямую в XSLT документе, таким вот незамысловатым образом:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <!-- simple script --><br>        <xsr:script><br>            <xsr:body><![CDATA[<br>                message('The Obligatory Hello World Example:');<br>                message('HELLO WORLD!');<br>            ]]></xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Передача параметров скрипту с помощью xsr:with-param


В некоторых случаях вам может понадобится передавать параметры объявленные извне в ваш JavaScript код. Для этих целей вы можете воспользоваться специальной инструкцией: xsr:with-param. Пример:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <!-- simple script --><br>        <xsr:script><br>            <xsr:with-param name="foo" select="string('bar')" /><br>            <xsr:with-param name="bar" select="local-name(.)" /><br>            <xsr:body><![CDATA[<br>                message('foo is:', getParam('foo'));<br>                message('bar is:', getParam('bar'));<br>            ]]></xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Внимание! В настоящий момент все что было передано в скрипт таким описанным образом — будет автоматически конвертировано в строку, так что передать фрагменты XML — документа таким способом не получится.

Использование xsr:body совместно с XSL — элементами


Вы можете замиксовать JavaScript и XSLT создав ситуацию когда ваш JavaScript код будет сгенерирован на лету таким образом:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <!-- script generate itself --><br>        <xsr:script><br>            <xsr:body><br>                message('\r');<br>                message('Script generate itself:');<br>                <xsl:for-each select="1 to 5"><br>                    message('position() = ' + <xsl:value-of select="position()" />);<br>                </xsl:for-each><br>            </xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Использование JavaScript для генерации результирующего XML документа


Используйте следующий пример для генерации результирующего XML документа с помощью JavaScript:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <!-- script that produces simple XML document as a result --><br>        <xsr:script><br>            <xsr:body><![CDATA[<br>                message();<br>                startElement('test');<br>                for (var c = 0; c < 10; c++) {<br>                    startElement('item_' + c);<br>                    characters('value_' + c);<br>                    endElement();<br>                }<br>                endElement();<br>            ]]></xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Сгенерированный XML документ:
<test><br>    <item_0>value_0</item_0><br>    <item_1>value_1</item_1><br>    <item_2>value_2</item_2><br>    <item_3>value_3</item_3><br>    <item_4>value_4</item_4><br>    <item_5>value_5</item_5><br>    <item_6>value_6</item_6><br>    <item_7>value_7</item_7><br>    <item_8>value_8</item_8><br>    <item_9>value_9</item_9><br></test><br><br>* This source code was highlighted with Source Code Highlighter.

Использование Java — классов в JavaScript коде


Вы можете использовать любые Java классы в вашем JavaScript коде, следующим образом:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <xsr:script><br>            <xsr:body><![CDATA[<br>                function buildDirectoryTree(sRootDirectory, sTrimDirectory) {<br>                    var aResultTree = [];<br>                    var aFiles = (new Packages.java.io.File(sRootDirectory)).listFiles();<br>                    for (var iFile = 0; iFile < aFiles.length; iFile++) {<br>                        var sFileName = String(aFiles[iFile]);<br>                        var bIsDirectory = aFiles[iFile].isDirectory();<br>                        if (!bIsDirectory) {<br>                            aResultTree.push(sFileName.substr((<br>                                sTrimDirectory ?<br>                                sTrimDirectory :<br>                                sRootDirectory<br>                            ).length));<br>                        } else {<br>                            aResultTree = aResultTree.concat(<br>                                buildDirectoryTree(<br>                                    sFileName,<br>                                    sRootDirectory<br>                                )<br>                            );<br>                        }<br>                    }<br>                    return aResultTree;<br>                }<br>                // Build directory tree<br>                var aDirectoryTree = buildDirectoryTree('.');<br>                for (var c = 0; c < aDirectoryTree.length; c++) {<br>                    message('Found directory:', aDirectoryTree[c]);<br>                }<br>            ]]></xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Вы также можете использовать классы которые не являются стандартной частью Java. Следующий пример показывает как можно использовать SQLiteJDBC (http://www.zentus.com/sqlitejdbc/), для соединения и работы с SQLite базой данных:
<?xml version="1.0" encoding="UTF-8"?><br><xsl:stylesheet version="2.0"<br>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br>    xmlns:xsr="java:/xsr.XSRElementFactory"<br>    extension-element-prefixes="xsr"><br><br>    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /><br><br>    <xsl:template match="//*"><br>        <xsr:script><br>            <xsr:body><![CDATA[<br>                function SQLite() {<br>                    Packages.java.lang.Class.forName('org.sqlite.JDBC');<br>                    this.connection = null;<br>                    this.connect = function(sDBFileName) {<br>                        this.connection = new Packages.java.sql.DriverManager.getConnection(<br>                            'jdbc:sqlite:' + sDBFileName<br>                        );<br>                        this.statement = this.connection.createStatement();<br>                    };<br>                    this.close = function() {<br>                        if (!this.connection) return;<br>                        this.connection.close();<br>                        this.statement.close();<br>                        this.connection = null;<br>                        this.statement = null;<br>                    };<br>                    this.query = function(sQuery) {<br>                        if (!this.statement) return;<br>                        var aResultSet = [];<br>                        try {<br>                            var oResultSet = this.statement.executeQuery(sQuery);<br>                        } catch (e) {}<br>                        if (oResultSet) {<br>                            while (oResultSet.next()) {<br>                                var aRowData = {};<br>                                for (var c = 1; c <= oResultSet.columnCount; c++) {<br>                                    var sColumnName = oResultSet.getColumnName(c);<br>                                    aRowData[sColumnName] = oResultSet.getString(<br>                                        sColumnName<br>                                    );<br>                                }<br>                                aResultSet.push(aRowData);<br>                            }<br>                            oResultSet.close();<br>                        }<br>                        return aResultSet;<br>                    };<br>                };<br>            ]]></xsr:body><br>        </xsr:script><br><br>        <!-- let's do something interesting with our database now --><br><br>        <xsr:script><br>            <xsr:body><![CDATA[<br>                message('SQLite example:\r\n');<br>                var oSQLite = new SQLite();<br>                oSQLite.connect('test.db');<br>                oSQLite.query('\<br>                    CREATE TABLE if not exists test_table (\<br>                        id INTEGER PRIMARY KEY AUTOINCREMENT,\<br>                        name TEXT\<br>                    );\<br>                ');<br>                for (var c = 0; c < 100; c++) {<br>                    oSQLite.query('INSERT INTO test_table (name) VALUES ("item_'+c+'")');<br>                }<br>                var aResult = oSQLite.query('SELECT * FROM test_table;');<br>                for (var c = 0; c < aResult.length; c++) {<br>                    startElement('row');<br>                    for (var sCellName in aResult[c]) {<br>                        startElement(sCellName);<br>                        characters(aResult[c][sCellName]);<br>                        endElement();<br>                    }<br>                    endElement();<br>                }<br>                oSQLite.query('DROP TABLE test_table');<br>                oSQLite.close();<br>            ]]></xsr:body><br>        </xsr:script><br>    </xsl:template><br></xsl:stylesheet><br><br>* This source code was highlighted with Source Code Highlighter.

Заключение


У вас может возникнуть вопрос зачем же все это надо? Ответ на него очень прост, XSLT совместно с XML это очень мощная связка которая может выполнять очень очень многие задачи, добавляя в это JavaScript и возможность простого запуска из командной строки с помощью Apache Ant, мы получаем мощное средство автоматизации с уже встроенным механизмом формирования конечного результата. Учитывая тот факт что для JavaScript сейчас написаны тонны кода которые выполняют разные прикладные задачи, данный проект (во всяком случае мне) уже не кажется на столько безумным.
Более реальный пример: нам нужно написать что то чтобы могло драть внешние ресурсы по HTTP протоколу, делать что нибудь с этим текстом — а затем закидывать XML отчет в базу или на другой сервер.

Это мой первый топик на Хабре а потому, любая конструктивная критика приветствуется. Также хотелось бы узнать у сообщества интересна ли вам данная тема и стоит ли мне опубликовать в ближайшее время что — нибудь подобное?

Ссылки

Tags:
Hubs:
+14
Comments 24
Comments Comments 24

Articles