Ключевые слова:xml, ruby, benchmark, (найти похожие документы)
From: Александр Неткачев <http://devlink.crimea.ua>
Date: Mon, 20 Sep 2004 18:21:07 +0000 (UTC)
Subject: Оптимальная обработка XML+XSL на Ruby
Оригинал: http://devlink.crimea.ua/articles/article.php?article_id=21
Обработка XML+XSL на Ruby
Александр Неткачев
19.05.2004
- Введение
- Обработка XML
- Постановка задачи
- Решение задачи методами rexml
- Решение задачи c помощью libgdome-ruby
- Победитель - Оболочка для libxml
- Вкусность на последок: XSL трансформация с использованием libxslt.
- Заключение
Введение
Изучая новую технологию всегда хочется применить её для решения
текущих задач. А одна из основных задач современного программиста -
это составление программ, которые собирают данные из нескольких
систем, обрабатывают их и выдают результат. Это напоминает сборку
модели из готовых деталей конструктора, где роль крупных блоков
играют, например, базы данных, а в качестве соединительных деталей
используется простой и гибкий язык программирования.
Основными источниками данных в современном программировании являются
текстовые файлы, базы данных и файлы в XML формате. А обрабатывать и
соединять их друг с другом попробуем с помощью скриптового языка
программирования Ruby.
Обработка текстовых файлов не представляет особой сложности, поскольку
возможности Ruby в области поддержки регулярных выражений делают
разбор любого текстового файла не очень сложной задачей. А вот
обработку XML документов можно рассмотреть подробнее.
Обработка XML
Для обработки XML существует как стандартное решение в виде rexml
библиотеки, которая входит в Ruby 1.8, так и альтернативные варианты,
которые в большинстве случаев представляют собой обертки (wrappers)
вокруг С библиотек libxml2 и производных от неё.
Поиск расширений Ruby для обработки XML документов
(http://raa.ruby-lang.org/search.rhtml?search=xml и на
http://libxml.org) приводит к множеству библиотек. Но отбросив все
alpha, unstable, experimental и тому подобное получаем совсем
небольшой список:
* libgdome-ruby (beta): http://raa.ruby-lang.org/project/libgdome-ruby/
* libxml (production quality): http://raa.ruby-lang.org/project/libxml/
* libxslt (working): [18]http://raa.ruby-lang.org/project/libxslt/
Я попробую сравнить найденные библиотеки между собой в решении простой
задачи.
Постановка задачи
Программа 1. Генерация XML документа (generateXml.rb)
puts '<nodes>'
1000.times {|i| puts '<node sum="1" avg="' + i.to_s + '">Node sample text</node>'}
puts '</nodes>'
Задача
В сгенерированном программой 1 XML документе пройтись по всем узлам,
посчитать сумму атрибутов sum и найти среднее значение атрибута avg.
Решение задачи методами rexml
Удобный API компенсируется недостатком производительности. Дело в том,
что библиотека rexml написана на самом Ruby, производительность
которого относительно C/C++ не велика. Также можно отметить, что XSL
трансформацию библиотека rexml не поддерживает.
Программа 2. Решение задачи (parseXml_2.rb)
require "rexml/document"
include REXML
xmlStr = ''
ARGF.each {|line| xmlStr << line}
doc = Document.new xmlStr
sum = avgSum = count = 0
doc.elements.each('/nodes/node') { |e|
count += 1
sum += e.attributes['sum'].to_i
avgSum += e.attributes['avg'].to_i
}
puts "count(node): #{count}, sum(sum): #{sum}, avg(avg): #{avgSum/count}"
Определение времени выполнения и результат
$ time ruby generateXml.rb | ruby parseXml_2.rb
count(node): 1000, sum(sum): 1000, avg(avg): 499
real 0m6.035s
user 0m5.450s
sys 0m0.590s
Итого - 6 секунд длился разбор документа на моем компьютере.
model name : Celeron (Mendocino)
cpu MHz : 534.552
cache size : 128 KB
bogomips : 1064.96
Решение задачи c помощью libgdome-ruby
libgdome-ruby-0.3.tar.bz2 представляет собой оболочку вокруг Gdome2
библиотеки. Gdome2 - это реализация W3C DOM Level2 на C. Поэтому для
установки libgdome-ruby надо сначала установить Gdome2 библиотеку.
Тут и пригодится Portage от Gentoo Linux:
$ emerge dev-libs/gdome2
Для ручной установки замечу, что Gdome2 зависит от библиотек libxml2
(http://xmlsoft.org/) и glib (http://www.gtk.org/). Сам Gdome2
можно скачать с сайта http://phd.cs.unibo.it/gdome2/
Далее переходим к установке Ruby расширения libgdome-ruby:
$ tar -xjf libgdome-ruby-0.3.tar.bz2
$ cd libgdome-ruby-0.3
$ ruby extconf.rb
$ make
$ make install
Программа 3. Решение задачи с использованием Gdome2 (parseXml_3.rb)
require "gdome"
xmlStr = ''
ARGF.each {|line| xmlStr << line}
domImpl = Dom::implementation
doc = domImpl.createDocFromMemory(xmlStr, 0)
sum = avgSum = count = 0
children = doc.documentElement.childNodes
(0...children.length).each{ |i|
el = children.item(i)
if (el.kind_of?(Dom::Element))
count += 1
sum += el.getAttribute('sum').to_i
avgSum += el.getAttribute('avg').to_i
end
}
puts "count(node): #{count}, sum(sum): #{sum}, avg(avg): #{avgSum/count}"
Можно задать резонный вопрос: почему я не использовал XPath, для
выборки узлов /Nodes/Node. Отвечаю - в библиотеке libgdome-ruby XPath
не реализовали, хотя в самой Gnome2 XPath присутствует в полной мере.
Отметим, что XSL трансформация не реализована в Gdom2 и, как
следствие, в libgdome-ruby тоже.
Время выполнения задачи с использованием gdome значительно лучше, чем
с rexml:
$ time ruby generateXml.rb | ruby parseXml_3.rb
count(node): 1000, sum(sum): 1000, avg(avg): 499
real 0m0.334s
user 0m0.300s
sys 0m0.030s
Победитель - Оболочка для libxml
Заявленая автором оболочки поддержка XPath и простые и напоминающие
rexml интерфейсы весьма привлекательны. Библиотека зависит от libm
(математические функции), libz (zlib), libiconv и, естественно, от
libxml2. Как правило, все эти библиотеки в современных дистрибутивах
есть, поэтому переходим без лишних слов к установке и реализации нашей
задачи:
Устанавливаем скачаный файл libxml-0.3.4.tar.gz:
$ tar -xzf libxml-0.3.4.tar.gz
$ cd libxml-0.3.4
$ ruby extconf.rb
$ make && make install
Программа 4. Решение задачи с использованием libxml (parseXml_4.rb)
require 'xml/libxml'
xmlStr = ''
ARGF.each {|line| xmlStr << line}
xp = XML::Parser.new()
xp.string = xmlStr
doc = xp.parse
sum = avgSum = count = 0
doc.find('/nodes/node').each { |e|
count += 1
sum += e['sum'].to_i
avgSum += e['avg'].to_i
}
puts "count(node): #{count}, sum(sum): #{sum}, avg(avg): #{avgSum/count}"
Традиционный замер времени выполнения:
$ time ruby generateXml.rb | ruby parseXml_4.rb
count(node): 1000, sum(sum): 1000, avg(avg): 499
real 0m0.210s
user 0m0.180s
sys 0m0.030s
Можно сказать только одно: Кубок победителю :-) Ruby libxml extention
показал лучший результат по эффективности и удобству интерфейсов.
Вкусность на последок: XSL трансформация с использованием libxslt.
Для установки libxslt требуется, что бы libxml уже было установлено и
header файлы находились в директории ../libxml относительно директории
с libxslt.
$ ln -s libxml-0.3.4 libxml
$ tar -xzf libxslt-0.3.4.tar.gz
$ cd libxslt-0.3.4
$ ruby extconf.rb
$ make && make install
Для примера предположим, что нам надо вывести список файлов в
директории. Довольно простая задача, но при этом требуется разделить
данные от представления. Это может понадобится, например, если дизайн
представления будет менятся.
Примерная реализация: получаем список файлов в директории, строим из
списка файлов XML и трансформируем его в HTML при помощи XSL.
XSL файл для трансформации (filesToHtml.xsl):
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html><body><ul>
<xsl:for-each select="/files/file">
<li><xsl:value-of select="."/></li>
</xsl:for-each>
</ul></body></html>
</xsl:template>
</xsl:stylesheet>
Ruby скрипт, осуществляющий трансформацию (buildFileList.rb):
require 'xml/libxml'
require 'xml/libxslt'
xslt = XML::XSLT.file('filesToHtml.xsl')
xp = XML::Parser.new
# замечатальный пример компакности Ruby - выполнение shell комманды,
# проход по строкам её результата и составление XML в одной строке :-)
xp.string = `ls`.inject('<files>') { |xml, file|
xml << '<file>' << file.chomp << '</file>' } + '</files>'
xslt.doc = xp.parse
s = xslt.parse
s.apply
s.print
Проверяем производительность:
$ time ruby buildFileList.rb
... результат трансформации пропущен ...
real 0m0.064s
user 0m0.030s
sys 0m0.030s
Заключение
Резюмируя, можно сказать, что Ruby на данный момент обладает
достаточными средствами для обработки XML документов и выполнения XSL
трансформаций. Он соединяет лучшее, что было сделано в
программировании - удобный синтаксис языка вместе с использованием
существующих библиотек. Полученное в результате решение отличается
простотой и легкостью в изучении, что делает его эффективным
инструментом для программистов.
круто. я смотрю ты серьзно решил затестить связку руби/хмл. а как насчет сравнения с джабой? я думаю было бы интересно. насколько мне известно, она тормозит на обработке хмл документво.
если не трудно и не лень .) - не мог бы и это продемонстрировать