Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Ruby.docx
Скачиваний:
19
Добавлен:
06.09.2019
Размер:
1.74 Mб
Скачать

Листинг 15.1. Файл books.Xml

<library shelf="Recent Acquisitions">

 <section name="Ruby">

  <book isbn="0672328844">

   <title>The Ruby Way</title>

   <author>Hal Fulton</author>

   <description>Second edition. The book you are now reading.

    Ain't recursion grand? </description>

  </book>

 </section>

 <section name="Space">

  <book isbn="0684835509">

   <title>The Case for Mars</title>

   <author>Robert Zubrin</author>

   <description>Pushing toward a second home for the human

    race. </description>

  </book>

  <book isbn="074325631X">

   <title>First Man: The Life of Neil A. Armstrong</title>

   <author>James R. Hansen</author>

   <description>Definitive biography of the first man on

    the moon. </description>

  </book>

 </section>

</library>

15.1.1. Древовидное представление

Сначала покажем, как работать с ХМL-документом, представленным в виде дерева. Для начала затребуем библиотеку rexml/document; обычно для удобства мы включаем также директивуinclude rexml, чтобы импортировать все необходимое в пространство имен верхнего уровня. В листинге 15.2 продемонстрировано несколько полезных приемов.

Листинг 15.2. Разбор документа с применением dom

require 'rexml/document'

include REXML

input = File.new("books.xml")

doc = Document.new(input)

root = doc.root

puts root.attributes["shelf"] # Недавние приобретения

doc.elements.each("library/section") { |e| puts e.attributes["name"] }

# Выводится:

#  Ruby

#  Space

doc.elements.each("*/section/book") { |e| puts e.attributes["isbn"] }

# Выводится:

#  0672328844

#  0321445619

#  0684835509

#  074325631X

sec2 = root.elements[2]

author = sec2.elements[1].elements["author"].text # Robert Zubrin

Обратите внимание: атрибуты представляются в виде хэша. Обращаться к элементам можно либо по пути, либо по номеру. В последнем случае учтите, что согласно спецификации XML индексация элементов начинается с 1, а не с 0, как в Ruby.

15.1.2. Потоковый разбор

А теперь попробуем разобрать тот же самый файл в потоковом стиле (на практике это вряд ли понадобилось бы, потому что размер файла невелик). У этого подхода несколько вариантов, в листинге 15.3 показан один из них. Идея в том, чтобы определить класс слушателя, методы которого анализатор будет вызывать для обработки событий.

Листинг 15.3. Sax-разбор

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*$/ # Ничего, кроме пропусков.

  abbrev = data[0..40] + (data.length > 40 ? "..." : "")

  puts "  text   :  #{abbrev.inspect}"

 end

end

list = MyListener.new

source = File.new "books.xml"

Document.parse_stream(source, list)

В этом нам поможет класс StreamListener; сам по себе он содержит только заглушки, то есть пустые методы обратного вызова. Вы должны переопределить их в своем подклассе. Когда анализатор встречает открывающий тег, он вызывает метод tag_open. Можете считать это чем-то вроде метода method_missing, которому в качестве параметра передается имя тега (и все его атрибуты в форме хэша). Аналогично работает метод text; о других методах вы можете прочитать в документации на сайте http://ruby-doc.org или в каком-нибудь другом месте.

Программа в листинге 15.3 протоколирует обнаружение каждого открывающего и каждого закрывающего тега. Результат работы показан в листинге 15.4 (для краткости текст приведен не полностью).