Ruby - Руководство по XML, XSLT и XPath

Что такое XML?

Extensible Markup Language (XML) - это язык разметки, очень похожий на HTML или SGML. Это рекомендовано Консорциумом World Wide Web и доступно в качестве открытого стандарта.

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

XML чрезвычайно полезен для отслеживания небольших и средних объемов данных, не требуя магистрали на основе SQL.

Архитектуры синтаксического анализатора XML и API

Для парсеров XML доступны два разных варианта:

  • SAX-like (Stream interfaces)- Здесь вы регистрируете обратные вызовы для интересующих событий, а затем позволяете синтаксическому анализатору продолжить работу с документом. Это полезно, когда ваши документы большие или у вас есть ограничения памяти, он анализирует файл при чтении его с диска, и весь файл никогда не сохраняется в памяти.

  • DOM-like (Object tree interfaces) - Это рекомендация Консорциума World Wide Web, согласно которой весь файл считывается в память и сохраняется в иерархической (древовидной) форме для представления всех функций XML-документа.

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

SAX доступен только для чтения, в то время как DOM позволяет вносить изменения в XML-файл. Поскольку эти два разных API буквально дополняют друг друга, нет причин, по которым вы не можете использовать их оба для больших проектов.

Разбор и создание XML с помощью Ruby

Самый распространенный способ манипулирования XML - это библиотека REXML Шона Рассела. С 2002 года REXML является частью стандартного дистрибутива Ruby.

REXML - это XML-процессор на чистом Ruby, соответствующий стандарту XML 1.0. Это непроверяющий процессор, прошедший все не подтверждающие тесты соответствия OASIS.

Парсер REXML имеет следующие преимущества перед другими доступными парсерами:

  • Он на 100% написан на Ruby.
  • Его можно использовать как для синтаксического анализа SAX, так и для DOM.
  • Это легкий, менее 2000 строк кода.
  • Методы и классы действительно просты для понимания.
  • API на основе SAX2 и полная поддержка XPath.
  • Поставляется с установкой Ruby, отдельная установка не требуется.

Для всех наших примеров кода XML давайте использовать в качестве входных данных простой файл XML -

<collection shelf = "New Arrivals">
   <movie title = "Enemy Behind">
      <type>War, Thriller</type>
      <format>DVD</format>
      <year>2003</year>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Talk about a US-Japan war</description>
   </movie>
   <movie title = "Transformers">
      <type>Anime, Science Fiction</type>
      <format>DVD</format>
      <year>1989</year>
      <rating>R</rating>
      <stars>8</stars>
      <description>A schientific fiction</description>
   </movie>
   <movie title = "Trigun">
      <type>Anime, Action</type>
      <format>DVD</format>
      <episodes>4</episodes>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Vash the Stampede!</description>
   </movie>
   <movie title = "Ishtar">
      <type>Comedy</type>
      <format>VHS</format>
      <rating>PG</rating>
      <stars>2</stars>
      <description>Viewable boredom</description>
   </movie>
</collection>

DOM-подобный парсинг

Давайте сначала проанализируем наши XML-данные в виде дерева . Начнем с требованияrexml/documentбиблиотека; часто мы включаем REXML для импорта в пространство имен верхнего уровня для удобства.

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Now get the root element
root = xmldoc.root
puts "Root element : " + root.attributes["shelf"]

# This will output all the movie titles.
xmldoc.elements.each("collection/movie"){ 
   |e| puts "Movie Title : " + e.attributes["title"] 
}

# This will output all the movie types.
xmldoc.elements.each("collection/movie/type") {
   |e| puts "Movie Type : " + e.text 
}

# This will output all the movie description.
xmldoc.elements.each("collection/movie/description") {
   |e| puts "Movie Description : " + e.text 
}

Это даст следующий результат -

Root element : New Arrivals
Movie Title : Enemy Behind
Movie Title : Transformers
Movie Title : Trigun
Movie Title : Ishtar
Movie Type : War, Thriller
Movie Type : Anime, Science Fiction
Movie Type : Anime, Action
Movie Type : Comedy
Movie Description : Talk about a US-Japan war
Movie Description : A schientific fiction
Movie Description : Vash the Stampede!
Movie Description : Viewable boredom

SAX-подобный синтаксический анализ

Чтобы обработать те же данные, файл movies.xml , в потоковом режиме, мы определим класс слушателя , методы которого будут целью обратных вызовов от парсера.

NOTE - Не рекомендуется использовать SAX-подобный синтаксический анализ для небольшого файла, это просто демонстрационный пример.

#!/usr/bin/ruby -w

require 'rexml/document'
require 'rexml/streamlistener'
include REXML

class MyListener
   include REXML::StreamListener
   def tag_start(*args)
      puts "tag_start: #{args.map {|x| x.inspect}.join(', ')}"
   end

   def text(data)
      return if data =~ /^\w*$/     # whitespace only
      abbrev = data[0..40] + (data.length > 40 ? "..." : "")
      puts "  text   :   #{abbrev.inspect}"
   end
end

list = MyListener.new
xmlfile = File.new("movies.xml")
Document.parse_stream(xmlfile, list)

Это даст следующий результат -

tag_start: "collection", {"shelf"=>"New Arrivals"}
tag_start: "movie", {"title"=>"Enemy Behind"}
tag_start: "type", {}
   text   :   "War, Thriller"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Talk about a US-Japan war"
tag_start: "movie", {"title"=>"Transformers"}
tag_start: "type", {}
   text   :   "Anime, Science Fiction"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "A schientific fiction"
tag_start: "movie", {"title"=>"Trigun"}
tag_start: "type", {}
   text   :   "Anime, Action"
tag_start: "format", {}
tag_start: "episodes", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Vash the Stampede!"
tag_start: "movie", {"title"=>"Ishtar"}
tag_start: "type", {}
tag_start: "format", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Viewable boredom"

XPath и Ruby

Альтернативный способ просмотра XML - XPath. Это своего рода псевдоязык, который описывает, как найти определенные элементы и атрибуты в XML-документе, рассматривая этот документ как логически упорядоченное дерево.

REXML поддерживает XPath через класс XPath . Он предполагает синтаксический анализ на основе дерева (объектная модель документа), как мы видели выше.

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Info for the first movie found
movie = XPath.first(xmldoc, "//movie")
p movie

# Print out all the movie types
XPath.each(xmldoc, "//type") { |e| puts e.text }

# Get an array of all of the movie formats.
names = XPath.match(xmldoc, "//format").map {|x| x.text }
p names

Это даст следующий результат -

<movie title = 'Enemy Behind'> ... </>
War, Thriller
Anime, Science Fiction
Anime, Action
Comedy
["DVD", "DVD", "DVD", "VHS"]

XSLT и Ruby

Доступны два парсера XSLT, которые может использовать Ruby. Здесь приводится краткое описание каждого из них.

Рубин-Саблотрон

Этот синтаксический анализатор написан и поддерживается Масаёши Такахаши. Это написано в основном для ОС Linux и требует следующих библиотек:

  • Sablot
  • Iconv
  • Expat

Вы можете найти этот модуль на Ruby-Sablotron.

XSLT4R

XSLT4R написан Майклом Нойманом, его можно найти в RAA в разделе «Библиотека» в разделе XML. XSLT4R использует простой интерфейс командной строки, хотя в качестве альтернативы его можно использовать в стороннем приложении для преобразования XML-документа.

XSLT4R требует для работы XMLScan, который включен в архив XSLT4R и также является полностью модулем Ruby. Эти модули могут быть установлены с использованием стандартного метода установки Ruby (например, ruby ​​install.rb).

XSLT4R имеет следующий синтаксис -

ruby xslt.rb stylesheet.xsl document.xml [arguments]

Если вы хотите использовать XSLT4R из приложения, вы можете включить XSLT и ввести необходимые параметры. Вот пример -

require "xslt"

stylesheet = File.readlines("stylesheet.xsl").to_s
xml_doc = File.readlines("document.xml").to_s
arguments = { 'image_dir' => '/....' }
sheet = XSLT::Stylesheet.new( stylesheet, arguments )

# output to StdOut
sheet.apply( xml_doc )

# output to 'str'
str = ""
sheet.output = [ str ]
sheet.apply( xml_doc )

Дальнейшее чтение

  • Для получения полной информации о парсере REXML, пожалуйста, обратитесь к стандартной документации по REXML Parser Documentation .

  • Вы можете загрузить XSLT4R из репозитория RAA .