Ruby 简明教程

Ruby - XML, XSLT and XPath Tutorial

What is XML?

可扩展标记语言 (XML) 是一种类似 HTML 或 SGML 的标记语言。它由万维网联盟推荐,并作为一个开放标准提供。

XML 是一种便携式开源语言,允许程序员开发应用程序,无论操作系统和/或开发语言是什么,其他应用程序都可以读取这些应用程序。

XML 极其适用于追踪少量至中等数量的数据,而不需要基于 SQL 的后端。

XML Parser Architectures and APIs

对于 XML 解析器,有两种不同的类型 −

  1. SAX-like (Stream interfaces) − 在此,你可以为你感兴趣的事件注册回调,然后让解析器继续处理文档。当你的文档很大,或者你有内存限制时,这很有用,它在从磁盘读取文件时对其进行解析,并且整个文件永远不会存储在内存中。

  2. DOM-like (Object tree interfaces) − 这是万维网联盟的建议,其中整个文件都被读入内存中,并存储在分层(基于树)的形式中来表示 XML 文档的所有特性。

在处理大型文件时,SAX 显然无法像 DOM 那样快速处理信息。另一方面,专属使用 DOM 会真正耗尽你的资源,尤其是在大量的小型文件中使用时。

SAX 是只读的,而 DOM 允许对 XML 文件进行更改。由于这两个不同的 API 实际上是互补的,因此你没有理由不能将它们都用于大型项目。

Parsing and Creating XML using Ruby

处理 XML 最常见的方法是使用 Sean Russell 的 REXML 库。自 2002 年以来,REXML 已成为标准 Ruby 发行版的一部分。

REXML 是一个符合 XML 1.0 标准的纯 Ruby XML 处理器。它是一个无需验证的处理器,通过了所有 OASIS 无需验证的一致性测试。

REXML 解析器相对于其他可用解析器具有以下优势 −

  1. 它 100% 用 Ruby 编写。

  2. 它可以用于 SAX 和 DOM 解析。

  3. 它很轻量级,代码少于 2000 行。

  4. 方法和类非常容易理解。

  5. 基于 SAX2 的 API 以及全部支持 XPath。

  6. 与 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-like Parsing

让我们首先以树状的方式解析我们的 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-like Parsing

要以面向流的方式处理相同的数据,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 and 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 and Ruby

Ruby 可使用两种 XSLT 解析器。这里对每种解析器做了简要说明。

Ruby-Sablotron

该解析器由 Masayoshi Takahashi 编写和维护。这主要针对 Linux 操作系统编写,需要以下库 −

  1. Sablot

  2. Iconv

  3. Expat

你可以在 Ruby-Sablotron 找到此模块。

XSLT4R

XSLT4R 由 Michael Neumann 编写,可在 XML 下的库部分中的 RAA 中找到。XSLT4R 使用一个简单的命令行界面,不过也可以在第三方应用程序中使用它来转换 XML 文档。

XSLT4R 需要 XMLScan 才能运行,该扫描器包含在 XSLT4R 归档文件中,并且也是一个 100% Ruby 模块。可以使用标准 Ruby 安装方法安装这些模块(即,ruby install.rb)。

XSLT4R 具有以下语法 −

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

如果你想在应用程序中使用 XSLT4R,你可以包括 XSLT 并输入所需的 parameters。以下为示例 −

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 )

Further Reading

  1. 有关 REXML 解析器的完整详细信息,请参阅 REXML Parser Documentation 的标准文档。

  2. 你可以从 RAA Repository 下载 XSLT4R。