Ruby - XML、XSLT 和 XPath 教程
什么是 XML?
可扩展标记语言 (XML) 是一种与 HTML 或 SGML 非常相似的标记语言。 这是万维网联盟推荐的,可作为开放标准使用。
XML 是一种可移植的开源语言,允许程序员开发可由其他应用程序读取的应用程序,而不管操作系统和/或开发语言如何。
XML 对于跟踪中小型数据量非常有用,而无需基于 SQL 的主干。
XML 解析器架构和 API
XML 解析器有两种不同的风格 −
SAX-like (Stream interfaces) − 在这里,您为感兴趣的事件注册回调,然后让解析器继续处理文档。 当您的文档很大或您有内存限制时,这很有用,它会在从磁盘读取文件时解析文件,并且整个文件永远不会存储在内存中。
DOM-like (Object tree interfaces) − 这是万维网联盟的建议,其中将整个文件读入内存并以分层(基于树)的形式存储,以表示 XML 文档的所有特征。
在处理大文件时,SAX 显然不能像 DOM 那样快速处理信息。 另一方面,仅使用 DOM 确实会杀死您的资源,尤其是在用于大量小文件时。
SAX 是只读的,而 DOM 允许更改 XML 文件。 由于这两个不同的 API 实际上是相互补充的,所以没有理由不能将它们都用于大型项目。
使用 Ruby 解析和创建 XML
操作 XML 的最常用方法是使用 Sean Russell 的 REXML 库。 自 2002 年以来,REXML 已成为标准 Ruby 发行版的一部分。
REXML 是符合 XML 1.0 标准的纯 Ruby XML 处理器。 它是一个非验证处理器,通过了所有 OASIS 非验证一致性测试。
REXML 解析器与其他可用解析器相比具有以下优点 −
- 它是 100% 用 Ruby 编写的。
- 它可以用于 SAX 和 DOM 解析。
- 它是轻量级的,不到 2000 行代码。
- 方法和类很容易理解。
- 基于 SAX2 的 API 和完整的 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 文件,我们将定义一个 listener 类,其方法将 成为解析器 callbacks 的目标。
注意 − 不建议对小文件使用类似 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
Ruby 可以使用两种 XSLT 解析器。 这里给出了每个的简要说明。
Ruby-Sablotron
此解析器由 Masayoshi Takahashi 编写和维护。 这主要是为 Linux 操作系统编写的,需要以下库 −
- Sablot
- Iconv
- Expat
您可以在 Ruby-Sablotron 找到此模块。
XSLT4R
XSLT4R 由 Michael Neumann 编写,可以在 RAA 的 XML 下的 Library 部分找到。 XSLT4R 使用简单的命令行界面,但也可以在第三方应用程序中使用它来转换 XML 文档。
XSLT4R 需要 XMLScan 才能运行,它包含在 XSLT4R 归档文件中,也是一个 100% 的 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 Parser 的完整详细信息,请参阅 REXML 解析器文档 的标准文档。
您可以从 RAA Repository 下载 XSLT4R。

