2009-07-28 6 views
64

Non riesco a trovare alcuna informazione su come analizzare documenti xml e accedere agli elementi.Clojure XML Parsing

Ho trovato due modi per analizzare il documento XML

(clojure.zip/xml-zip (clojure.xml/parse file)) 

e

(parse-seq file) 

ma io riesco a trovare alcuna info su come elaborare la struttura risultante?

Il file di origine si riferisce a zip-query.clj su come interrogare il risultato ma sembra che manchi anche questo.

+2

Gli esempi da zip-query.clj possono essere trovati in xml_test.clj che si trova in src/test/clojure/clojure/data/zip nel repository github per clojure.data.zip –

+0

Funny, ho chiesto questo come bene, e ha ottenuto ottime risposte da alcune delle persone più utili su SO. Tuttavia, anche dopo aver eseguito i dati analizzati risultanti.xml attraverso uno dei suggerimenti, la struttura risultante non ha ancora molto senso per me. Vado a dare un'occhiata al tuo xml-zip, a meno che data.xml non sia il suo successore. – octopusgrabbus

risposta

81

Si supponga di avere il seguente codice XML per analizzare nel file:

<high-node> 
    <low-node>my text</low-node> 
</high-node> 

si carica clojure.xml:

user=> (use 'clojure.xml) 

quando analizzato, il xml avrà la seguente struttura:

{:tag :high-node, :attrs nil, :content [{:tag :low-node, :attrs nil, :content ["my text"]}]} 

e quindi è possibile vedere il contenuto del file per ottenere il contenuto di e low-node:

user=> (for [x (xml-seq 
       (parse (java.io.File. file))) 
       :when (= :low-node (:tag x))] 
     (first (:content x))) 

("my text") 

Allo stesso modo, se si voleva avere accesso a tutta la lista delle informazioni sul livello basso-nodo, avrebbe cambiato il :when predicato (= (:high-node (:tag x))):

user=> (for [x (xml-seq 
       (parse (java.io.File. file))) 
       :when (= :high-node (:tag x))] 
     (first (:content x))) 

({:tag :low-node, :attrs nil, :content ["my text"]}) 

Questo funziona perché le parole chiave può funzionare come funzioni. Vedere Questions about lists and other stuff in Clojure e Data Structures: Keywords

+0

Ottima risposta! –

+0

Spiegazione molto bella! Ho intenzione di provare questo. – Ralph

+0

(dichiarazione di non responsabilità: sono una novità di clojure) ... Ma ho trovato che questo ha funzionato per me nella REPL, e non ho potuto farlo funzionare in un file (la mia ignoranza è la colpa). Clojure.data.zip.xml ha funzionato per me in un file senza modifiche. – wonderfulthunk

52

La risposta di cui sopra funziona, ma lo trovo molto più facile da usare clojure.data.zip.xml (usato per essere clojure-contrib.zip-filter.xml prima Clojure 1.3).

di file:

myfile.xml:

<songs> 
    <track id="t1"><name>Track one</name></track> 
    <track id="t2"><name>Track two</name></track> 
</songs> 

codice:

; Clojure 1.3 
(ns example 
    (:use [clojure.data.zip.xml :only (attr text xml->)]) ; dep: see below 
    (:require [clojure.xml :as xml] 
      [clojure.zip :as zip])) 

(def xml (xml/parse "myfile.xml")) 
(def zipped (zip/xml-zip xml)) 
(xml-> zipped :track :name text)  ; ("Track one" "Track two") 
(xml-> zipped :track (attr :id))  ; ("t1" "t2") 

Purtroppo, è necessario tirare in una dipendenza da data.zip per ottenere questo bel funzionalità di lettura/filtro. Vale la pena la dipendenza :) In lein sarebbe (a partire dal 17-ago-2013):

[org.clojure/data.zip "0.1.1"] 

E per quanto riguarda la documentazione di data.zip.xml ... Mi basta guardare il relativamente piccolo file sorgente here a guarda cosa è possibile Un'altra buona risposta SO è here.

+2

Questo ha funzionato alla grande per me in un progetto a cui sto lavorando. – wonderfulthunk

+1

Non capisco perché (xml-> zippato: traccia: nome testo) funzionerà, ma (xml-> zippato: canzoni: traccia: nome testo) non funzionerà o (xml-> zippato: nome testo) non funzionerà. Non sei sicuro del motivo per cui devi specificare un certo livello di nidificazione dei tag ma non di altri. –

+0

@RyanMoore è un [zipper] (http://www.haskell.org/haskellwiki/Zipper), le cerniere sono sensibili al contesto in quanto hanno un nodo corrente e si devono distribuire le istruzioni di attraversamento in modo appropriato rispetto a quel nodo di contesto. Apparentemente il nodo di contesto predefinito è root che ha senso. –