Ruby XML, XSLT 和 XPath 教程
XML(可扩展标记语言)是一种用于存储和传输数据的标记语言。Ruby提供了强大的XML处理能力,包括解析、生成、转换和查询XML文档。
📋 本章内容
- XML基础概念
- Ruby中的XML处理库
- 解析XML文档
- 生成XML文档
- XPath查询
- XSLT转换
- 实际应用示例
🔧 XML处理库
Ruby提供了多个XML处理库:
REXML(Ruby内置)
ruby
require 'rexml/document'Nokogiri(推荐)
ruby
# 需要先安装: gem install nokogiri
require 'nokogiri'📖 XML基础概念
什么是XML?
XML是一种标记语言,用于描述数据的结构和内容:
xml
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<title>Ruby编程指南</title>
<author>张三</author>
<price currency="CNY">89.00</price>
<category>编程</category>
</book>
<book id="2">
<title>Web开发实战</title>
<author>李四</author>
<price currency="CNY">99.00</price>
<category>Web开发</category>
</book>
</bookstore>🔍 使用REXML解析XML
基本解析
ruby
require 'rexml/document'
# XML字符串
xml_string = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<title>Ruby编程指南</title>
<author>张三</author>
<price>89.00</price>
</book>
</bookstore>
XML
# 创建文档对象
doc = REXML::Document.new(xml_string)
# 获取根元素
root = doc.root
puts "根元素: #{root.name}"
# 遍历子元素
root.elements.each('book') do |book|
puts "书籍ID: #{book.attributes['id']}"
puts "标题: #{book.elements['title'].text}"
puts "作者: #{book.elements['author'].text}"
puts "价格: #{book.elements['price'].text}"
puts "---"
end从文件读取XML
ruby
require 'rexml/document'
# 从文件读取
file = File.new("books.xml")
doc = REXML::Document.new(file)
file.close
# 处理文档...🏗️ 生成XML文档
使用REXML生成XML
ruby
require 'rexml/document'
# 创建新文档
doc = REXML::Document.new
doc.add_element('xml-stylesheet', {
'type' => 'text/xsl',
'href' => 'books.xsl'
})
# 创建根元素
root = doc.add_element('bookstore')
# 添加书籍
book1 = root.add_element('book', {'id' => '1'})
book1.add_element('title').add_text('Ruby编程指南')
book1.add_element('author').add_text('张三')
book1.add_element('price', {'currency' => 'CNY'}).add_text('89.00')
book2 = root.add_element('book', {'id' => '2'})
book2.add_element('title').add_text('Web开发实战')
book2.add_element('author').add_text('李四')
book2.add_element('price', {'currency' => 'CNY'}).add_text('99.00')
# 输出XML
formatter = REXML::Formatters::Pretty.new
formatter.compact = true
formatter.write(doc, $stdout)🔎 XPath查询
XPath是用于在XML文档中查找信息的语言。
基本XPath语法
ruby
require 'rexml/document'
xml_string = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1" category="programming">
<title>Ruby编程指南</title>
<author>张三</author>
<price>89.00</price>
</book>
<book id="2" category="web">
<title>Web开发实战</title>
<author>李四</author>
<price>99.00</price>
</book>
</bookstore>
XML
doc = REXML::Document.new(xml_string)
# 查找所有书籍标题
titles = REXML::XPath.match(doc, "//title")
titles.each { |title| puts title.text }
# 查找特定ID的书籍
book = REXML::XPath.first(doc, "//book[@id='1']")
puts "找到书籍: #{book.elements['title'].text}"
# 查找价格大于90的书籍
expensive_books = REXML::XPath.match(doc, "//book[price > 90]")
expensive_books.each do |book|
puts "昂贵的书: #{book.elements['title'].text}"
end
# 查找编程类别的书籍
programming_books = REXML::XPath.match(doc, "//book[@category='programming']")
programming_books.each do |book|
puts "编程书籍: #{book.elements['title'].text}"
end常用XPath表达式
ruby
# 选择根元素
root = REXML::XPath.first(doc, "/bookstore")
# 选择所有book元素
books = REXML::XPath.match(doc, "//book")
# 选择第一个book元素
first_book = REXML::XPath.first(doc, "//book[1]")
# 选择最后一个book元素
last_book = REXML::XPath.first(doc, "//book[last()]")
# 选择有id属性的book元素
books_with_id = REXML::XPath.match(doc, "//book[@id]")
# 选择包含特定文本的元素
ruby_books = REXML::XPath.match(doc, "//book[contains(title, 'Ruby')]")🔄 XSLT转换
XSLT(可扩展样式表语言转换)用于将XML文档转换为其他格式。
安装libxslt
bash
# Ubuntu/Debian
sudo apt-get install libxslt1-dev
# macOS
brew install libxslt
# 安装Ruby gem
gem install nokogiriXSLT转换示例
ruby
require 'nokogiri'
# XML数据
xml_string = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<title>Ruby编程指南</title>
<author>张三</author>
<price>89.00</price>
</book>
<book id="2">
<title>Web开发实战</title>
<author>李四</author>
<price>99.00</price>
</book>
</bookstore>
XML
# XSLT样式表
xslt_string = <<-XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head><title>书店目录</title></head>
<body>
<h1>书店目录</h1>
<table border="1">
<tr>
<th>ID</th>
<th>标题</th>
<th>作者</th>
<th>价格</th>
</tr>
<xsl:for-each select="bookstore/book">
<tr>
<td><xsl:value-of select="@id"/></td>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="author"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSLT
# 执行转换
xml_doc = Nokogiri::XML(xml_string)
xslt_doc = Nokogiri::XSLT(xslt_string)
result = xslt_doc.transform(xml_doc)
puts result.to_s🚀 使用Nokogiri(推荐)
Nokogiri是Ruby中最流行的XML/HTML处理库,性能更好,功能更强大。
安装Nokogiri
bash
gem install nokogiri基本使用
ruby
require 'nokogiri'
# 解析XML
xml_string = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<title>Ruby编程指南</title>
<author>张三</author>
<price currency="CNY">89.00</price>
</book>
</bookstore>
XML
doc = Nokogiri::XML(xml_string)
# CSS选择器
title = doc.css('title').first
puts "标题: #{title.content}"
# XPath查询
author = doc.xpath('//author').first
puts "作者: #{author.content}"
# 属性访问
book = doc.css('book').first
puts "书籍ID: #{book['id']}"
price = doc.css('price').first
puts "价格: #{price.content} #{price['currency']}"修改XML
ruby
require 'nokogiri'
doc = Nokogiri::XML(xml_string)
# 修改文本内容
title = doc.css('title').first
title.content = "Ruby高级编程指南"
# 修改属性
book = doc.css('book').first
book['id'] = '100'
# 添加新元素
category = Nokogiri::XML::Node.new('category', doc)
category.content = '编程语言'
book.add_child(category)
puts doc.to_xml📝 实际应用示例
RSS阅读器
ruby
require 'nokogiri'
require 'open-uri'
class RSSReader
def initialize(url)
@url = url
end
def read_feed
doc = Nokogiri::XML(URI.open(@url))
title = doc.css('channel title').first.content
puts "RSS源: #{title}"
puts "=" * 50
doc.css('item').each_with_index do |item, index|
puts "#{index + 1}. #{item.css('title').first.content}"
puts " 链接: #{item.css('link').first.content}"
puts " 时间: #{item.css('pubDate').first&.content}"
puts " 描述: #{item.css('description').first&.content&.strip}"
puts "-" * 30
end
end
end
# 使用示例
# reader = RSSReader.new('https://example.com/rss.xml')
# reader.read_feed配置文件处理器
ruby
require 'nokogiri'
class ConfigManager
def initialize(config_file)
@config_file = config_file
@doc = File.exist?(config_file) ?
Nokogiri::XML(File.read(config_file)) :
create_default_config
end
def get_setting(key)
node = @doc.css("setting[name='#{key}']").first
node ? node['value'] : nil
end
def set_setting(key, value)
node = @doc.css("setting[name='#{key}']").first
if node
node['value'] = value
else
root = @doc.root
setting = Nokogiri::XML::Node.new('setting', @doc)
setting['name'] = key
setting['value'] = value
root.add_child(setting)
end
end
def save
File.write(@config_file, @doc.to_xml)
end
private
def create_default_config
Nokogiri::XML(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
XML
end
end
# 使用示例
config = ConfigManager.new('app_config.xml')
config.set_setting('database_host', 'localhost')
config.set_setting('database_port', '5432')
config.save
puts "数据库主机: #{config.get_setting('database_host')}"⚠️ 常见问题和注意事项
编码问题
ruby
# 确保正确处理中文编码
xml_string = <<-XML.force_encoding('UTF-8')
<?xml version="1.0" encoding="UTF-8"?>
<data>
<message>你好,世界!</message>
</data>
XML
doc = Nokogiri::XML(xml_string)
puts doc.css('message').first.content命名空间处理
ruby
xml_with_namespace = <<-XML
<?xml version="1.0"?>
<root xmlns:book="http://example.com/book">
<book:title>Ruby编程</book:title>
</root>
XML
doc = Nokogiri::XML(xml_with_namespace)
# 注册命名空间
doc.remove_namespaces! # 简单方法:移除所有命名空间
title = doc.css('title').first.content
# 或者正确处理命名空间
doc = Nokogiri::XML(xml_with_namespace)
title = doc.xpath('//book:title', 'book' => 'http://example.com/book').first.content性能优化
ruby
# 对于大型XML文件,使用SAX解析器
class BookHandler < Nokogiri::XML::SAX::Document
def start_element(name, attributes = [])
if name == 'book'
@current_book = {}
attributes.each { |attr| @current_book[attr[0]] = attr[1] }
end
end
def characters(string)
@current_text = string.strip if string.strip.length > 0
end
def end_element(name)
case name
when 'title'
@current_book[:title] = @current_text
when 'author'
@current_book[:author] = @current_text
when 'book'
puts "处理书籍: #{@current_book}"
end
end
end
# 使用SAX解析器
parser = Nokogiri::XML::SAX::Parser.new(BookHandler.new)
parser.parse(File.open('large_books.xml'))🎯 最佳实践
选择合适的解析器:
- 小文件:使用DOM解析器(Nokogiri::XML)
- 大文件:使用SAX解析器
- HTML:使用Nokogiri::HTML
错误处理:
ruby
begin
doc = Nokogiri::XML(xml_string)
if doc.errors.any?
puts "XML解析错误:"
doc.errors.each { |error| puts " #{error}" }
end
rescue => e
puts "处理XML时发生错误: #{e.message}"
end- 内存管理:
ruby
# 处理完大型文档后释放内存
doc = nil
GC.start📚 学习资源
通过本章的学习,你已经掌握了Ruby中XML处理的核心技能。这些知识将帮助你处理配置文件、数据交换、Web服务等各种实际应用场景。