2010-05-20 57 views
9

我很困惑,因爲如何通過clojure.contrib的zip-filter.xml來習慣性地更改xml樹。應該試圖做到這一點,還是有更好的方法?在Clojure中的XML文件中插入到Zipper樹中

說,我有一些虛擬的xml文件 「itemdb.xml」 是這樣的:

<itemlist> 
    <item id="1"> 
    <name>John</name> 
    <desc>Works near here.</desc> 
    </item> 
    <item id="2"> 
    <name>Sally</name> 
    <desc>Owner of pet store.</desc> 
    </item> 
</itemlist> 

而且我有一些代碼:

(require '[clojure.zip :as zip] 
    '[clojure.contrib.duck-streams :as ds] 
    '[clojure.contrib.lazy-xml :as lxml] 
    '[clojure.contrib.zip-filter.xml :as zf]) 

(def db (ref (zip/xml-zip (lxml/parse-trim (java.io.File. "itemdb.xml"))))) 

;; Test that we can traverse and parse. 
(doall (map #(print (format "%10s: %s\n" 
     (apply str (zf/xml-> % :name zf/text)) 
     (apply str (zf/xml-> % :desc zf/text)))) 
    (zf/xml-> @db :item))) 

;; I assume something like this is needed to make the xml tags 
(defn create-item [name desc] 
    {:tag :item 
    :attrs {:id "3"} 
    :contents 
    (list {:tag :name :attrs {} :contents (list name)} 
     {:tag :desc :attrs {} :contents (list desc)})}) 

(def fred-item (create-item "Fred" "Green-haired astrophysicist.")) 

;; This disturbs the structure somehow 
(defn append-item [xmldb item] 
    (zip/insert-right (-> xmldb zip/down zip/rightmost) item)) 

;; I want to do something more like this 
(defn append-item2 [xmldb item] 
    (zip/insert-right (zip/rightmost (zf/xml-> xmldb :item)) item)) 

(dosync (alter db append-item2 fred-item)) 

;; Save this simple xml file with some added stuff. 
(ds/spit "appended-itemdb.xml" 
    (with-out-str (lxml/emit (zip/root @db) :pad true))) 

我不清楚如何使用Clojure的。 zip在這種情況下適當地運作,以及如何與zip-過濾器交互。

如果您在這個小例子中發現任何特別怪異的東西,請指出。

+0

的contrib.zip過濾器.xml現在轉移到https://github.com/clojure/data.zip/ – claj 2013-08-17 10:56:14

回答

8

首先,您應該在Fred的定義中使用:content(而不是:contents)。

有了這種變化,下面似乎工作:

(-> (zf/xml-> @db :item) ; a convenient way to get to the :item zipper locs 
    first    ; but we actually need just one 
    zip/rightmost  ; let's move to the rightmost sibling of the first :item 
         ; (which is the last :item in this case) 
    (zip/insert-right fred-item) ; insert Fred to the right 
    zip/root)   ; get the modified XML map, 
         ; which is the root of the modified zipper 

append-item2很相似,只有兩個更正,使:

  1. zf/xml->返回拉鍊的序列LOCS; zip/rightmost只接受一個,所以你必須先釣一個(因此上面的first);

  2. 修改完拉鍊後,您需要使用zip/root返回底層樹的(修改後的版本)。

由於款式最後一點,print + format = printf。 :-)

+0

感謝您的更正!這裏仍然有一些奇怪的行爲,似乎通過在代碼中的最後一個zip/root之後添加zip/xml-zip來解決。不添加額外的行可以防止你做多個append ...顯然zip/root不會返回一個拉鍊,只是另一個位置,所以append-item返回的類型仍然不是很正確,但是很好足以讓lxml /在最後發出... 不得不笑笑關於printf的筆記...驚人地有多少次你可以盯着看不到明顯的東西。 ;-) – ivar 2010-05-21 06:02:21

5

在您錯誤輸入的創作項目中:內容爲:內容,您應該更喜歡向量來列出文字。

(我打算做一個比較全面的答案,但作爲米哈爾已經寫一個相當不錯)

到拉鍊過濾器的替代方案是Enlive:

(require '[net.cgrand.enlive-html :as e]) ;' <- fix SO colorizer 

(def db (ref (-> "itemdb.xml" java.io.File. e/xml-resource)) 

(defn create-item [name desc] 
    {:tag :item 
    :attrs {:id "3"} 
    :content [{:tag :name :attrs {} :content [name]} 
      {:tag :desc :attrs {} :content [desc]}]}) 

(def fred-item (create-item "Fred" "Green-haired astrophysicist.")) 

(dosync (alter db (e/transformation [:itemlist] (e/append fred-item)))) 
+1

看完[http://github.com/swannodette/enlive-tutorial/]後,我將不得不仔細看看... – ivar 2010-05-21 03:07:03