XML處理也是個(gè)常見的編程工作,雖然說在Clojure里你很少使用XML做配置文件,但是跟遺留系統(tǒng)集成或者處理和其他系統(tǒng)通訊,可能都需要處理XML。
Clojure的標(biāo)準(zhǔn)庫clojure.xml就是用來干這個(gè)事情的。一個(gè)簡單的例子如下,首先我們要解析的是下面這個(gè)簡單的XML:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>The joy of clojure</title>
<author>Michael Fogus / Chris House</author>
</book>
<book>
<title>Programming clojure</title>
<author>Stuart Halloway</author>
</book>
<book>
<title>Practical clojure</title>
<author>Luke Van der Hart</author>
</book>
</books>
解析xml用clojure.xml/parse方法即可,該方法返回一個(gè)clojure.xml/element這個(gè)struct-map組成的一棵樹:
user=> (use '[clojure.xml])
nil
user=> (parse "test.xml")
{:tag :books, :attrs nil, :content
[{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]}
這是一個(gè)嵌套的數(shù)據(jù)結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都是clojure.xml/element結(jié)構(gòu),element包括:
(defstruct element :tag :attrs :content)
tag、attrs和content屬性,tag就是該節(jié)點(diǎn)的標(biāo)簽,attrs是一個(gè)屬性的map,而content是它的內(nèi)容或者子節(jié)點(diǎn)。element是一個(gè)struct map,它也定義了三個(gè)方法來分別獲取這三個(gè)屬性:
user=> (def x (parse "test.xml"))
#'user/x
user=> (tag x)
:books
user=> (attrs x)
nil
user=> (content x)
[{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]
books節(jié)點(diǎn)是root node,它的content就是三個(gè)book子節(jié)點(diǎn),子節(jié)點(diǎn)組織成一個(gè)vector,我們可以隨意操作:
user=> (tag (first (content x)))
:book
user=> (content (first (content x)))
[{:tag :title, :attrs nil, :content ["The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]
user=> (content (first (content (first (content x)))))
["The joy of clojure"]
額外提下,clojure.xml是利用SAX API做解析的。同樣它還有個(gè)方法,可以將解析出來的結(jié)構(gòu)還原成xml,通過emit:
user=> (emit x)
<?xml version='1.0' encoding='UTF-8'?>
<books>
<book>
<title>
The joy of clojure
</title>
<author>
Michael Fogus / Chris House
</author>
</book>
<book>

如果你要按照深度優(yōu)先的順序遍歷xml,可以利用xml-seq將解析出來的樹構(gòu)成一個(gè)按照深度優(yōu)先順序排列節(jié)點(diǎn)的LazySeq,接下來就可以按照seq的方式處理,比如利用for來過濾節(jié)點(diǎn):
user=> (for [node (xml-seq x)
:when (= :author (:tag node))]
(first (:content node)))
("Michael Fogus / Chris House" "Stuart Halloway" "Luke Van der Hart")
通過:when指定了條件,要求節(jié)點(diǎn)的tag是author,這樣就可以查找出所有的author節(jié)點(diǎn)的content,是不是很方便?就像寫英語描述一樣。
更進(jìn)一步,如果你想操作parse解析出來的這棵樹,你還可以利用clojure.zip這個(gè)標(biāo)準(zhǔn)庫,它有xml-zip函數(shù)將xml轉(zhuǎn)換成zipper結(jié)構(gòu),并提供一系列方法來操作這棵樹:
user=>(def xz (xml-zip x))
#'user/xz
user=> (node (down xz))
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
user=> (-> xz down right node)
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
user=> (-> xz down right right node)
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}
user=> (-> xz down right right lefts)
({:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]})
是不是酷得一塌糊涂?可以通過up,down,left,right,lefts,rights,來查找節(jié)點(diǎn)的鄰近節(jié)點(diǎn),可以通過node來得到節(jié)點(diǎn)本身。一切顯得那么自然。更進(jìn)一步,你還可以“編輯“這棵樹,比如刪除The joy of clojure這本書:
user=> (def loc-in-new-tree (remove (down xz)))
#'user/loc-in-new-tree
user=> (root loc-in-new-tree)
{:tag :books, :attrs nil, :content
[{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content ["Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]}
ok,只剩下兩本書了,更多方法還包括replace做替換,edit更改節(jié)點(diǎn)等。因此編輯XML并重新生成,你一般可以利用clojure.zip來更改樹,最后利用clojure.xml/emit將更改還原為xml。
生成xml除了emit方法,還有一個(gè)contrib庫,也就是
prxml,這個(gè)庫的clojure 1.3版本有人維護(hù)了一個(gè)分支,在
這里。主要方法就是prxml,它可以將clojure的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換成xml:
user=>(prxml [:p [:raw! "<i>here & gone</i>"]])
<p><i>here & gone</i></p>
顯然,它也可以用于生成HTML。
xpath的支持可以使用
clj-xpath這個(gè)開源庫,遺憾的是它目前僅支持clojure 1.2。
轉(zhuǎn)載請注明出處:
http://www.tkk7.com/killme2008/archive/2012/02/18/370233.html