2010-05-20 10 views
9

No estoy seguro de cómo cambiar idiomáticamente un árbol xml al que se accede a través de zip-filter.xml de clojure.contrib. ¿Debería tratar de hacer esto en absoluto, o hay una mejor manera?Inserciones en árboles Zipper en archivos XML en Clojure

decir que tengo algún archivo XML de prueba "itemdb.xml" de esta manera:

<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> 

y tengo algo de código:

(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))) 

no tengo muy claro acerca de cómo usar la clojure. zip funciona apropiadamente en este caso, y cómo eso interactúa con zip-filter.

Si detecta algo particularmente extraño en este pequeño ejemplo, por favor indíquelo.

+0

la contrib.zip-filtro .xml ahora se movió a https://github.com/clojure/data.zip/ – claj

Respuesta

8

En primer lugar, debe usar :content (y no :contents) en su definición de Fred.

Con ese cambio en su lugar, la siguiente parece funcionar:

(-> (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 

Su append-item2 es muy similar, sólo hay dos correcciones para hacer:

  1. zf/xml-> devuelve una secuencia de cremallera locs; zip/rightmost acepta solo uno, por lo que tiene que pescar primero (de ahí el first en el anterior);

  2. Después de que haya terminado de modificar la cremallera, debe usar zip/root para recuperar (la versión modificada) del árbol subyacente.

Como nota final en el estilo, print + = formatprintf. :-)

+0

¡Gracias por las correcciones! Todavía hay un comportamiento extraño aquí que parece resolverse al agregar zip/xml-zip después del zip/root final en su código. Si no agrega esa línea adicional, evita que haga más de un apéndice ... al parecer, zip/root no devuelve una cremallera, solo otra ubicación, por lo que el tipo devuelto por el elemento anexado no será del todo correcto, pero bueno suficiente para el lxml/emitir al final ... Tuve que reírme de la nota printf ... increíble cuántas veces se puede mirar algo sin ver lo obvio. ;-) – ivar

5

En el elemento de creación que escribiste mal: contenido para: contenido y debes preferir vectores a listas para literales.

(. Iba a hacer una respuesta más integral, pero Michal como ya se ha escrito bastante buena)

Una alternativa a zip-filtro es 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

Tendré que darle una mirada más cercana después de mirar [http://github.com/swannodette/enlive-tutorial/] ... – ivar