2010-08-08 13 views
22

Ho una pazza idea, che consiste nel mettere un codice di clojure in CouchDB e scrivere viste che lo interrogano. Non voglio memorizzare il codice del clojure come testo normale, perché in quel caso dovrei preoccuparmi di analizzarlo nelle viste. La formattazione e i commenti non devono essere conservati, ma il codice dovrebbe essere in grado di entrare e uscire dal database senza modificare la struttura. Parole chiave, simboli e stringhe dovrebbero rimanere tutti nel loro tipo nativo. Inoltre, voglio che il codice sia elegante ed efficiente.Come mappare il codice clojure da e verso JSON?

sto pensando di rappresentare le cose come segue:

  • simboli come stringhe che iniziano con '
  • Parole come stringhe che iniziano con:
  • Strings non modificato, tranne quando iniziano con' o : nel qual caso sono scappati con una barra rovesciata.
  • (parentesi) come una matrice
  • [parentesi] come un array con "_ []" come il primo elemento
  • mappe ({}) come un oggetto
  • set (# {}) come oggetto con i valori impostati su 1 e "_ # {}" incluso.

Critiche, esperienze e idee sono apprezzate.

Edit: Ecco cosa succede se provo la lettura e la scrittura di codice JSON utilizzando le funzioni JSON da clojure.contrib:

user> code 
((ns bz.json.app (:use (ring.middleware file))) (defn hello [req] {:status 200, :headers {"Content-Type" "text/plain"}, :body "Hello World!"}) (def app (wrap-file hello "public"))) 
user> (read-json (json-str code)) 
[["ns" "bz.json.app" ["use" ["ring.middleware" "file"]]] ["defn" "hello" ["req"] {"body" "Hello World!", "headers" {"Content-Type" "text/plain"}, "status" 200}] ["def" "app" ["wrap-file" "hello" "public"]]] 

C'è un bel po 'che deve essere fatto per la linea 4 della sopra per essere esattamente come la linea 2. Sembra che sia un progetto di libreria, a meno che non ci sia una funzione da qualche parte che non conosca.

Con una tale biblioteca, ecco cosa chiamarlo potrebbe essere simile:

user> (= (json-to-code (read-json (json-str (code-to-json code)))) code) 
true 
+0

Mi piacerebbe contrassegnare questa domanda come risposta, ma le risposte finora non lo coprono davvero! Sto cercando un esempio di come deserializzare e serializzare su JSON, preservando i tipi che il lettore Clojure comprende. Ciò richiederà metadati. Scusa se si ottiene questa pagina quando si sta cercando un serializzatore/deserializzatore JSON di base per Clojure. Non è di questa domanda. –

risposta

7

Penso che la tua idea è sana, ma mi piacerebbe semplificare la gestione delle collezioni utilizzando matrici etichettate (["list", …], ["vector", …]). A parte questo, non cambierei la strategia di implementazione.

Mi piace la tua idea e codice in Clojure, quindi ho preso una coltellata per implementare il tuo code-to-json (con il suggerimento sopra incorporato) al https://gist.github.com/3219854.

'la potenza che genera:

(code-to-json example-code) 
; => ["list" ["list" "'ns" "'bz.json.app" ["list" ":use" ["list" "'ring.middleware" "'file"]]] ["list" "'defn" "'hello" ["vector" "'req"] {":status" 200, ":headers" {"Content-Type" "text/plain"}, ":body" "Hello World!"}] ["list" "'def" "'app" ["list" "'wrap-file" "'hello" "public"]]] 

json-to-code è lasciata come esercizio per il lettore. ;)

+1

Mi piace la tua attenzione ai dettagli. :) Sembra abbastanza robusto per me. –

8

Se si desidera utilizzare JSON come rappresentazione, avrei fortemente suggerisco di usare clojure.contrib.json, che fa già il lavoro di conversione Clojure strutture dati a JSON abbastanza perfettamente.

Nessun punto di reinventare la ruota :-)

ho usato abbastanza successo nel mio progetto Clojure corrente. Se non fa tutto quello che vuoi, allora puoi sempre contribuire con una patch per migliorarla!

+0

Non ho intenzione di scrivere un parser per JSON, quindi probabilmente userò clojure.contrib.json. Sto cercando un modo per mappare il codice arbitrario del clojure dentro e fuori una struttura di dati abbastanza semplice da essere convertito senza perdita da e verso JSON. –

9

Come mikera suggerito, clojure.contrib.json/write-JSON convertirà non solo tipi primitivi, ma di Clojure ISeq s e Map s di Java, Collection S e Array s, anche. Questo dovrebbe coprire la maggior parte del codice (visto come dati), ma nel caso in cui si vuole scrivere qualcosa di più elaborato, è facile estendere lo scrittore JSON, imitando il codice sorgente di Stuart Sierra (vedi here):

(defn- write-json-fancy-type [x #^PrintWriter out] 
    (write-json-string (str x) out)) ;; or something useful here! 

(extend your-namespace.FancyType clojure.contrib.json/Write-JSON 
    {:write-json write-json-fancy-type}) 

Si presume che non sia necessario memorizzare il bytecode calcolato o le chiusure catturate. Questo sarebbe un gioco completamente diverso, molto più difficile. Ma dal momento che la maggior parte del codice Clojure (come la maggior parte dei Lisp's) può essere visto come un albero/foresta di S-Expressions, dovresti essere OK.

L'analisi di JSON sui dati può essere eseguita con clojure.contrib.json/read-json (richiedere un po 'di tempo per visualizzare le opzioni sulla sua definizione, si consiglia di utilizzarle). Successivamente, eval potrebbe essere il tuo migliore amico.

+0

Questo in realtà non funziona, perché "write-json-string" è una funzione privata. Quello che puoi fare è: (defn-write-json-fancy-type [x #^PrintWriter out] (.print out (json-str (str x)))) – zippy

+0

esattamente, è una funzione privata che dovrebbe essere definita scrivere qualcosa di utile - come il .print che hai citato. Non mi riferivo a una funzione specifica che è privata in una biblioteca - mi dispiace per l'eventuale deviazione. – Edgar

+0

Potresti creare un esempio? – sneilan

4

Per completezza c'è anche clj-json che utilizza Jackson sotto per analizzare il JSON.

0

Con la versione 0.1.2 di clojure.data.json il tutto potrebbe assomigliare a questo immagino:

(require ['clojure.data.json :as 'json]) 

(defn- json-write-date [s ^java.io.PrintWriter out escape-unicode?] 
    (.write out (str "\"" 
    (.format (java.text.SimpleDateFormat. "yyyyMMddHHmmssZ") s) "\""))) 

(extend java.util.Date clojure.data.json/Write-JSON {:write-json json-write-date}) 

(json/json-str { :example (java.util.Date.)}) 
"{\"example\":\"20120318182612+0100\"}"` 
5

clojure.contrib.json è stata sostituita da clojure.data.json:

(require '[clojure.data.json :as json]) 

(json/write-str {:a 1 :b 2}) 
;;=> "{\"a\":1,\"b\":2}" 

(json/read-str "{\"a\":1,\"b\":2}") 
;;=> {"a" 1, "b" 2} 

come si potrebbe anche utilizzare cheshire che ha una bella API e il supporto per varie estensioni come la codifica personalizzata e SMILE (binario JSON):

(:require [cheshire.core :as json]) 

(json/encode {:a 1 :b 2}) 
;;=> "{\"a\":1,\"b\":2}" 

(json/decode "{\"a\":1,\"b\":2}") 
;;=> {"a" 1, "b" 2} 
Problemi correlati