Я смущен как, как идиоматически измениться, дерево XML получило доступ через zip-filter.xml clojure.contrib. Должен пытаться сделать это вообще или там лучший путь?
Скажите, что у меня есть некоторый фиктивный 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.
При определении чего-либо особенно странного в этом небольшом примере укажите на это.
Во-первых, вы должны использовать : 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
очень похож, нужно внести всего два исправления:
zf / xml ->
возвращает последовательность локаторов застежки-молнии; zip / крайний правый
принимает только один, поэтому вам нужно сначала выловить один (отсюда первый
в приведенном выше примере);
после того, как вы закончите изменять застежку-молнию, вам нужно использовать zip / root
, чтобы вернуться к (модифицированной версии) лежащего в основе дерева.
В качестве последнего примечания к стилю: print
+ format
= printf
. : -)
В create-item вы неправильно ввели :contents для :content и вам следует предпочесть векторы спискам для литералов.
(Я собирался сделать более полный ответ, но Михал уже написал довольно хороший ответ.)
Альтернативой zip-фильтру является 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))))