2012-05-15 45 views
2

我有一個需要將Clojure數據結構序列化爲XML,但我絆倒如何發出一個集合。爲了使這更具體,假設我有一個Clojure的地圖如下:使用clojure.data.xml從Clojure發送XML。

(def parking-lot {:parking-lot #{ 
         {:car {:color "red", :make "nissan", :year 2003}} 
         {:car {:color "blue", :make "toyota", :year 2001}} 
         {:car {:color "black", :make "honda", :year 2010}}}}) 

這只是一個元素,它的值是一組「汽車」項目地圖。現在,讓我們假設,我想序列化這個地圖生成以下XML:

<? xml version="1.0" ?> 
    <parking-lot> 
     <car color="red" make="nissan" year="2003" /> 
     <car color="blue" make="toyota" year="2001" /> 
     <car color="black" make="black" year="2010" /> 
    </parking-lot> 

在網上搜索有關如何最好地解析/ Clojure中發出XML文檔導致我clojure.data.xml庫,所以這就是我正在使用。

這裏有一個如何使用clojure.data.xml發出一些XML一個簡單的例子:

REPL> (use 'clojure.data.xml) 

     => nil 
    REPL> (emit-str (element :parking-lot {})) 

     => "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 
      <parking-lot></parking-lot>" 

稍微複雜一點的例子:

REPL> (emit-str (element :parking-lot {} 
       (element :car {:color "yellow" :make "ford" :year "2000"}) 
       (element :car {:color "purple" :make "chevrolet" :year "1977"}))) 

     => "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 
      <parking-lot> 
       <car color=\"yellow\" year=\"2000\" make=\"ford\"></car> 
       <car color=\"purple\" year=\"1977\" make=\"chevrolet\"></car> 
      </parking-lot>" 

這裏可以看到,第三個參數「元素」功能的確可以是對「元素」的可變數量的調用。沒有深入研究'元素'函數的文檔,我想我認爲'元素'會被重載,也會將一系列元素項作爲第三個參數。所以,不看文件,我只是說幹就幹,掀起了一個很小的代碼塊,將做到這一點:

REPL> (emit-str (element :parking-lot {} 
       (map (fn [car] (let [carattrs (:car car)] 
         (element :car {:color (:color carattrs), 
             :make (:make carattrs), 
             :year (:year carattrs)}))) 
        (:parking-lot parking-lot)))) 

我希望這會工作,但可惜事實並非如此。問題是'元素'函數的第三個參數不能是序列本身。

所以,在這一點上我有點難以理解該怎麼做。我對Lisp和Clojure來說還是比較新的,所以如果我的下一個想法是虛無go go的,那就輕鬆點。所以我的想法是,這是一個宏的用例嗎?也就是說,我是否應該創建一個宏作爲其輸入的一個集合(或任何關於該事件的集合)的汽車,並將其作爲單獨的s表達式輸出?因此,例如,我想在我的行爲類似這樣的宏記:

REPL> (defmacro flatten-as-elements [coll-of-cars] ...) 
    REPL> (flatten-as-elements #{ {:color "blue" :model "honda" year "2000"} 
            {:color "yellow" :model "ford" year "2011"}}) 
     => (element :car {:color "blue" :model "honda" year "2000"}) 
      (element :car {:color "yellow" :model "ford" year "2011"}) 

在我的腦海裏,至少,這樣的宏的輸出將適合作爲第三個參數的「元素」功能,並會產生我期望的目標。當然,我的擔心是,我忽略了一些完全明顯的解決方案,並且在這裏使用宏是錯誤的。非常感謝SO社區的任何幫助!先謝謝你!

- 保羅

回答

5

您可以使用apply拼接參數到一個函數調用:

(emit-str 
(apply element :parking-lot {} 
     (map (fn [car] (let [carattrs (:car car)] 
         (element :car {:color (:color carattrs), 
             :make (:make carattrs), 
             :year (:year carattrs)}))) 
          (:parking-lot parking-lot)))) 
6

你自找的轉型不是一般的可能,因爲你有什麼應該是一個屬性的,什麼是一個子元素「隱藏」的信息。你可以編寫這個自定義的flatten-as-elements函數,或者任何它,然後你可以序列化,如果你在整個過程中插入一堆特定於領域的知識。

不過說真的,你應該使用XML的Clojure中的兩個流行的「交涉」的一個:要麼寫你的數據

{:tag :parking-lot 
:content [{:tag :car :attrs {:color "red", :make "nissan", :year 2003}} 
      {:tag :car :attrs {:color "blue", :make "toyota", :year 2001}} 
      {:tag :car :attrs {:color "black", :make "honda", :year 2010}}]} 

我認爲數據。XML可以發出你相當簡單,否則使用更簡潔的風格打嗝,與標籤的載體,並列出多個元素:

[:parking-lot (list [:car {:color "red", :make "nissan", :year 2003}] 
        [:car {:color "blue", :make "toyota", :year 2001}] 
        [:car {:color "black", :make "honda", :year 2010}])] 

,您可以用(emit (sexp-as-element [...]))打印。