以樹(shù)方式使用 REXML
REXML 的目的是 正好夠用。在最大程度上,它能很好地完成任務(wù)。 實(shí)際上, REXML 支持兩種不同樣式的 XML 處理 ― “樹(shù)”和“流”。 第一種樣式是 DOM 所嘗試要做的更簡(jiǎn)單的版本;第二種樣式是 SAX 所嘗試要做的更簡(jiǎn)單的版本。 讓我們先研究樹(shù)樣式。假設(shè)我們要提取上一個(gè)示例中的同一個(gè)地址簿文檔。 下面的示例來(lái)自我所創(chuàng)建的經(jīng)修改的 eval.rb ; 標(biāo)準(zhǔn) eval.rb (鏈接到 Ruby 教程)可以根據(jù)對(duì)復(fù)雜對(duì)象的表達(dá)式求值顯示非常長(zhǎng)的計(jì)算結(jié)果 ― 我的 eval.rb 在沒(méi)有錯(cuò)誤發(fā)生的情況下不作出反應(yīng):
如何使用 REXML 來(lái)引用嵌套數(shù)據(jù)
ruby> require "rexml/document"
ruby> include REXML
ruby> addrbook = (Document.new File.new "address.xml").root
ruby> persons = addrbook.elements.to_a("http://person")
ruby> puts persons[1].elements["address"].attributes["city"]
New York
這個(gè)表達(dá)式很普通。 .to_a() 方法創(chuàng)建文檔中所有 person> 元素的數(shù)組,在其它命名中它可能是有用的。 元素有點(diǎn)象 DOM 節(jié)點(diǎn),但它其實(shí)更接近于 XML 本身(而且使用起來(lái)也更簡(jiǎn)單)。 .to_a() 的參數(shù)是 XPath,在這種情況下,可以標(biāo)識(shí)文檔中任何地方的所有 person> 元素。如果我們只需要第一層上的元素,可以使用:
創(chuàng)建匹配元素的數(shù)組
ruby> persons = addrbook.elements.to_a("/addressbook/person")
我們甚至可以更直接地將 XPath 用作 .elements 屬性的重載索引。例如:
使用 REXML 來(lái)引用嵌套數(shù)據(jù)的另一種方法
ruby> puts addrbook.elements["http://person[2]/address"].attributes["city"]
New York
請(qǐng)注意,XPath 使用基于 1 的索引,不象 Ruby 和 Python 數(shù)組使用基于 0 的索引。換句話說(shuō), 它仍是我們正在檢查其所在城市的同一個(gè)人。通過(guò)查看 REXML 請(qǐng)注意,XPath 使用基于 1 的索引,不象 Ruby 和 Python 數(shù)組使用基于 0 的索引。換句話說(shuō), 它仍是我們正在檢查其所在城市的同一個(gè)人。通過(guò)查看
用 REXML 顯示元素的 XML 源代碼
ruby> puts addrbook.elements["http://person[2]/address"]
address city='New York' street='118 St.' number='344' state='NY'/>
ruby> puts addrbook.elements["http://person[2]/contact-info"]
contact-info>
email address='robb@iro.ibm.com'/>
home-phone number='03-3987873'/>
/contact-info>
此外,XPath 不必只與一個(gè)元素匹配。我們已在定義 persons 數(shù)組時(shí)看見(jiàn)過(guò),但另一個(gè)示例強(qiáng)調(diào)了這一點(diǎn):
將多個(gè)元素與 XPath 匹配
ruby> puts addrbook.elements.to_a("http://person/address[@state='CA']")
address city='Sacramento' street='Spruce Rd.' number='99' state='CA'/>
address city='Los Angeles' street='Pine Rd.' number='1234' state='CA'/>
與此相反, .elements 屬性的索引只產(chǎn)生 第一個(gè)匹配的元素:
當(dāng) XPath 只匹配第一次出現(xiàn)時(shí)
ruby> puts addrbook.elements.to_a("http://person/address[@state='CA']")
address city='Sacramento' street='Spruce Rd.' number='99' state='CA'/>
address city='Los Angeles' street='Pine Rd.' number='1234' state='CA'/>
也可以通過(guò) REXML 中的 XPath 類(lèi)使用 XPath 地址, 它具有諸如 .first() 、 .each() 和 .match() 這樣的方法。
REXML 元素的一個(gè)獨(dú)特的慣用方法是 .each 迭代器。雖然 Ruby 有一個(gè)可對(duì)集合進(jìn)行操作的循環(huán)結(jié)構(gòu) for , 但 Ruby 程序員通常更喜歡使用迭代器方法來(lái)將控制傳遞給代碼塊。下面的兩種結(jié)構(gòu)是等價(jià)的, 但第二種結(jié)構(gòu)有更為自然的 Ruby 感覺(jué):
通過(guò)在 REXML 中匹配 XPath 進(jìn)行迭代
ruby> for addr in addrbook.elements.to_a("http://address[@state='CA']")
| puts addr.attributes["city"]
| end
Sacramento
Los Angeles
ruby> addrbook.elements.each("http://address[@state='CA']") {
| |addr| puts addr.attributes["city"]
| }
Sacramento
Los Angeles
以流方式使用 REXML
出于“正好夠用”的目的, REXML 的樹(shù)方式可能是 Ruby 語(yǔ)言最簡(jiǎn)單的方法。 但 REXML 還提供了一種流方式,它象是 SAX 的更輕量級(jí)的變體。 正如使用 SAX 一樣, REXML 沒(méi)有向應(yīng)用程序程序員提供來(lái)自 XML 文檔的缺省數(shù)據(jù)結(jié)構(gòu)。 相反,“l(fā)istener”或“handler”類(lèi)負(fù)責(zé)提供響應(yīng)文檔流中各種事件的一組方法。 以下是常用集合:開(kāi)始標(biāo)記、結(jié)束標(biāo)記、遇到的元素文本等等。
雖然流方式遠(yuǎn)遠(yuǎn)沒(méi)有象以樹(shù)方式工作那樣容易,但通常它的速度要快很多。 REXML 教程聲稱流方式的速度要快 1500倍。 雖然我沒(méi)有嘗試過(guò)對(duì)它進(jìn)行基準(zhǔn)測(cè)試,但我猜想這是一種有限的情況(我的小示例在樹(shù)方式中也是瞬間完成的)。 總之,如果速度要緊,那么速度上的差異很可能是顯著的。
讓我們研究一個(gè)非常簡(jiǎn)單的示例,它所做的事情與上面的“列出加州城市”示例相同。 對(duì)它進(jìn)行擴(kuò)展以用于復(fù)雜的文檔處理相對(duì)比較簡(jiǎn)單:
REXML 中 XML 文檔的流處理
ruby> require "rexml/document"
ruby> require "rexml/streamlistener"
ruby> include REXML
ruby> class Handler
| include StreamListener
| def tag_start name, attrs
| if name=="address" and attrs.assoc("state")[1]=="CA"
| puts attrs.assoc("city")[1]
| end
| end
| end
ruby> Document.parse_stream((File.new "address.xml"), Handler.new)
Sacramento
Los Angeles
流處理示例中要注意的一件事情是,標(biāo)記屬性被作為一組數(shù)組傳遞, 它要處理的工作比起散列要稍微多一點(diǎn)(但可能在庫(kù)中創(chuàng)建會(huì)更快)。
編碼問(wèn)題
REXML所有文本節(jié)點(diǎn)中都是以UTF-8編碼的,所有調(diào)用的代碼都要注意這一點(diǎn),在程序中,傳遞給REXML的字符串必須是經(jīng)過(guò)UTF-8編碼的。
REXML不可能總是正確猜測(cè)出你的文本的編碼方式,所以它總是假定為UTF-8編碼。同時(shí),如果你試圖添加其他編碼方式的文本,REXML不會(huì)發(fā) 出警告。添加者必須保證自己添加的是UTF-8的文本。如果添加標(biāo)準(zhǔn)的ASCII 7位編碼,是沒(méi)有關(guān)系的。如果使用ISO8859-1文本,必須在添加之前轉(zhuǎn)換為UTF-8編碼??梢允褂胻ext.unpack("C").pack("U")。變更編碼進(jìn)行輸出,只有Document.write()和Document.to_s() 支持。如果需要輸出特定編碼的節(jié)點(diǎn),必須用Output把輸出對(duì)象包裝起來(lái)。
e = Element.new "a/>"
e.text = "f\xfcr" # ISO-8859-1 '??'
o = ''
e.write( Output.new( o, "ISO-8859-1" ) )
可以向Output傳遞任何支持的編碼。
您可能感興趣的文章:- Ruby中使用Nokogiri包來(lái)操作XML格式數(shù)據(jù)的教程
- 實(shí)例解析Ruby程序中調(diào)用REXML來(lái)解析XML格式數(shù)據(jù)的用法
- Ruby使用REXML庫(kù)來(lái)解析xml格式數(shù)據(jù)的方法
- Ruby程序中創(chuàng)建和解析XML文件的方法
- 在Ruby中處理XML和XSLT以及XPath的簡(jiǎn)單教程
- Ruby的XML格式數(shù)據(jù)解析庫(kù)Nokogiri的使用進(jìn)階