Ruby 操作 XML, XSLT 、XPath
XML 指可扩展标记语言 (eXtensible Markup Language)
XML 是一种用于标记电子文件使其具有结构性的标记语言
XML 可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言,它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据
更多 XML 相关的知识,可以移步通过 XML 基础教程
XML 解析器结构 和 API
XML 的解析器主要有 DOM 和 SAX 两种
-
SAX 解析器是基于事件处理的,需要从头到尾把 XML 文档扫描一遍,在扫描的过程中,每次遇到一个语法结构时,就会调用这个特定语法结构的事件处理程序,向应用程序发送一个事件
-
DOM 是文档对象模型解析,构建文档的分层语法结构,在内存中建立 DOM 树,DOM 树的节点以对象的形式来标识,文档解析文成以后,文档的整个 DOM 树都会放在内存中
Ruby 语言解析和创建 XML
Ruby 语言可以使用 REXML
库来解析 XML 文档
REXML 库是 Ruby 的一个 XML 工具包,使用纯 Ruby 语言编写的,遵守 XML1.0规范
从 Ruby 1.8 开始, Ruby 标准库中已经包含 REXML
REXML 库的加载路径是: rexml/document
REXML 库的优点
REXML 解析器比其它的解析器有以下优点
- 100% 由 Ruby 编写
- 可适用于 SAX 和 DOM 解析器
- 它是轻量级的,不到 2000 行代码
- 很容易理解的方法和类
- 基于 SAX2 API 和完整的 XPath 支持
- 使用 Ruby 安装,而无需单独安装
假设存在 movies.xml
文件,文件内容如下
movies.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 解析器
main.rb
# !/usr/bin/ruby -w # -*- encoding:utf-8 -*- # filename: main.rb # author: 简单教程(www.twle.cn) # Copyright © 2015-2065 www.twle.cn. All rights reserved. require 'rexml/document' include REXML xmlfile = File.new("movies.xml") xmldoc = Document.new(xmlfile) # 获取 root 元素 root = xmldoc.root puts "Root element : " + root.attributes["shelf"] # 以下将输出电影标题 xmldoc.elements.each("collection/movie"){ |e| puts "Movie Title : " + e.attributes["title"] } # 以下将输出所有电影类型 xmldoc.elements.each("collection/movie/type") { |e| puts "Movie Type : " + e.text } # 以下将输出所有电影描述 xmldoc.elements.each("collection/movie/description") { |e| puts "Movie Description : " + e.text }
运行以上 Ruby 范例,输出结果如下
$ ruby main.rb 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:
SAX 解析器
main.rb
# !/usr/bin/ruby -w # -*- encoding:utf-8 -*- # filename: main.rb # author: 简单教程(www.twle.cn) # Copyright © 2015-2065 www.twle.cn. All rights reserved. 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)
运行以上范例,输出结果如下
$ ruby main.rb 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"
不推荐 SAX 解析 XML 小文件
XPath 和 Ruby
我们还可以使用 XPath 解析 XML
XPath 是一种用于在 XML 文档中查询信息的语言
XPath 即为 XML 路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言 XPath 基于 XML 的树状结构,提供在数据结构树中找寻节点的能力
更多 XPath 知识,请移步 XPath 基础教程
Ruby 语言通过 REXML 的 XPath 类支持 XPath,它是基于树的分析
main.rb
# !/usr/bin/ruby -w # -*- encoding:utf-8 -*- # filename: main.rb # author: 简单教程(www.twle.cn) # Copyright © 2015-2065 www.twle.cn. All rights reserved. require 'rexml/document' include REXML xmlfile = File.new("movies.xml") xmldoc = Document.new(xmlfile) # 第一个电影的信息 movie = XPath.first(xmldoc, "//movie") p movie # 打印所有电影类型 XPath.each(xmldoc, "//type") { |e| puts e.text } # 获取所有电影格式的类型,返回数组 names = XPath.match(xmldoc, "//format").map {|x| x.text } p names
运行以上范例,输出结果如下
$ ruby main.rb <movie title='Enemy Behind'> ... </> War, Thriller Anime, Science Fiction Anime, Action Comedy ["DVD", "DVD", "DVD", "VHS"]
Ruby 和 XSLT
Ruby 语言中中有两个 XSLT 解析器
1. Ruby-Sablotron
Ruby-Sablotron 解析器主要是为 Linux 操作系统编写的,它依赖以下库:
- Sablot
- Iconv
- Expat
2. XSLT4R
XSLT4R 用于简单的命令行交互,可以被第三方应用程序用来转换 XML 文档
XSLT4R 需要 XMLScan 操作,包含了 XSLT4R 归档, XSLT4R 是一个100%的 Ruby 的模块
这些模块可以使用标准的 Ruby 安装方法(Ruby install.rb)进行安装
XSLT4R 语法格式如下:
ruby xslt.rb stylesheet.xsl document.xml [arguments]
main.rb
# !/usr/bin/ruby -w # -*- encoding:utf-8 -*- # filename: main.rb # author: 简单教程(www.twle.cn) # Copyright © 2015-2065 www.twle.cn. All rights reserved. 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 解析器文档