Comment mapper le code clojure vers et depuis JSON?
J'ai une idée folle, qui consiste à mettre du code clojure dans CouchDB et à écrire des vues qui l'interrogent. Je ne veux pas stocker le code clojure en texte brut, car alors je devrais m'inquiéter de l'analyser dans les vues. Le formatage et les commentaires n'ont pas besoin d'être conservés, mais le code devrait pouvoir entrer et sortir de la base de données sans changer de structure. Les mots-clés, les symboles et les chaînes doivent tous rester dans leur type natif. De plus, je veux que le code soit élégant et être efficace.
Je pense à représenter les choses comme suit:
- Symboles sous forme de chaînes commençant par '
- mots-clés en tant que chaînes commençant par:
- chaînes non modifiées, sauf quand elles commencent par ' or:, auquel cas elles sont échappées avec une barre oblique inverse.
- (parens) comme un tableau
- [crochets] comme un tableau avec "_ [] " comme premier élément
- mappe ({}) en tant qu'objet
- définit (#{}) comme un objet avec les valeurs définies aux 1 et "_#{}" inclus.
Les Critiques, les expériences et les idées sont appréciées.
Edit : voici ce qui se passe si j'essaie de lire et d'écrire du code JSON en utilisant les fonctions json de 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"]]]
Il y a un peu juste qui doit être fait pour que la ligne 4 de ce qui précède soit exactement comme la ligne 2. Il semble que c'est un projet de bibliothèque, à moins qu'il y ait une fonction quelque part qui le fait que je ne connais pas.
Avec une telle bibliothèque, voici ce qu'elle pourrait appeler comme:
user> (= (json-to-code (read-json (json-str (code-to-json code)))) code)
true
6 réponses
Je pense que votre idée est saine, mais je simplifierais la gestion des collections en utilisant des tableaux marqués(["list", …]
, ["vector", …]
) au lieu de cela. En dehors de cela, je ne changerais pas la stratégie de mise en œuvre.
J'aime votre idée et coder dans Clojure, alors j'ai pris un coup de poignard pour implémenter votre code-to-json
(avec la suggestion ci-dessus incorporée) à https://gist.github.com/3219854 .
C'est la sortie qu'il génère:
(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
est laissé comme exercice pour le lecteur. ;)
Comme mikera l'a suggéré, clojure.contrib.json /write-json convertira non seulement les types primitifs, mais aussi les ISeq
S de Clojure et les Map
S de Java, Collection
s et Array
s de Java. Cela devrait couvrir la plupart de votre code (considéré comme des données), mais dans le cas où vous voulez écrire quelque chose de plus fantaisiste, il est facile d'étendre l'écrivain JSON, en imitant le code source de Stuart Sierra (voir ici):
(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})
Cela suppose que vous n'avez pas besoin de stocker le bytecode calculé ou les fermetures capturées. Ce serait un jeu tout à fait différent, beaucoup plus difficile. Mais puisque la plupart du code Clojure (comme la plupart des Lisp) peut être vu comme un arbre / forêt de S-Expressions , Vous devriez être OK.
Analyser JSON vers les données peut être fait avec clojure.contrib.json/read-json
(prenez un peu de temps pour regarder les options sur sa définition, vous pouvez les utiliser). Après cela, eval
peut être votre meilleur ami.
Si vous voulez utiliser JSON comme représentation, je suggère fortement d'utiliser clojure.contrib.json , qui fait déjà le travail de convertir les structures de données Clojure en JSON de manière assez transparente.
Pas de point réinventer la roue: -)
Je l'ai utilisé assez bien dans mon projet Clojure actuel. Si elle ne fait pas tout ce que vous voulez, alors vous pouvez toujours contribuer un patch pour l'améliorer!
clojure.contrib.json
a été remplacée par 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}
Vous pouvez également utiliser cheshire
qui a une belle API et un support pour diverses extensions telles que custom encoding et SMILE (binary 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}
Avec la version 0.1.2 de clojure.données.json le tout pourrait ressembler à ceci je suppose:
(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\"}"`