Как сопоставить код clojure от JSON и от него?
У меня сумасшедшая идея, которая включает в себя вставку некоторого кода clojure в CouchDB и запись просмотров, которые его запрашивают. Я не хочу хранить код clojure как обычный текст, потому что тогда мне придется беспокоиться о разборе его в представлениях. Форматирование и комментарии не нужно сохранять, но код должен иметь возможность входить и выходить из базы данных без изменения структуры. Ключевые слова, символы и строки должны оставаться в их родном типе. Кроме того, я хочу, чтобы код выглядел элегантным и эффективным.
Я собираюсь представить вещи следующим образом:
- Символы как строки, начинающиеся с '
- Ключевые слова как строки, начинающиеся с:
- Строки немодифицированы, кроме случаев, когда они начинаются с 'или:, и в этом случае они экранируются с обратной косой чертой.
- (parens) как массив
- [скобки] как массив с "_ []" в качестве первого элемента
- maps ({}) как объект
- устанавливает (# {}) как объект со значениями, установленными в 1 и включенными "_ # {}".
Критика, опыт и идеи оценены.
Изменить. Что произойдет, если я попытаюсь прочитать и написать код JSON с помощью json-функций из 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"]]]
Там справедливый бит, который нужно сделать для строки 4 выше, чтобы быть точно такой же, как строка 2. Кажется, что это проект библиотеки, если там нет функции где-то, которая делает это, о которой я не знаю.
С такой библиотекой здесь, как бы это выглядело:
user> (= (json-to-code (read-json (json-str (code-to-json code)))) code)
true
Ответы
Ответ 1
Я думаю, что ваша идея звучит, но я упростил бы обработку коллекций, используя тегированные массивы (["list", …]
, ["vector", …]
). Кроме того, я бы не изменил стратегию реализации.
Мне нравится ваша идея и код в Clojure, поэтому я взял удар по реализации вашего code-to-json
(с приведенным выше предложением) в https://gist.github.com/3219854.
Это результат, который он генерирует:
(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
остается в качестве упражнения для читателя.;)
Ответ 2
Как предположил мика, clojure.contrib.json/write-json будет конвертировать не только примитивные типы, но Clojure ISeq
и Java Map
s, Collection
и Array
s. Это должно охватывать большую часть вашего кода (рассматривается как данные), но в случае, если вы хотите написать что-нибудь более интересное, легко расширить JSON-писатель, переработав исходный код Stuart Sierra (см. здесь):
(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})
Предполагается, что вам не нужно хранить вычисленный байт-код или захваченные блокировки. Это будет совершенно другая игра, значительно сложнее. Но так как большинство Clojure кода (например, большинство Lisp) можно рассматривать как дерево/лес S-Expressions, вы должны ОК.
Разбор JSON обратно к данным может быть выполнен с помощью clojure.contrib.json/read-json
(найдите время, чтобы посмотреть параметры в его определении, вы можете использовать их). После этого eval
может быть вашим лучшим другом.
Ответ 3
Если вы хотите использовать JSON в качестве представления, я бы настоятельно предложил использовать clojure.contrib.json, который уже выполняет работа по преобразованию структур данных Clojure в JSON довольно легко.
Нет смысла изобретать колесо: -)
Я использовал его довольно успешно в моем текущем проекте Clojure. Если он не делает все, что вам нужно, вы всегда можете внести исправление, чтобы улучшить его.
Ответ 4
clojure.contrib.json
был заменен на 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}
Вы также можете использовать cheshire
, который имеет хороший API и поддерживает различные расширения, такие как пользовательская кодировка и SMILE (двоичный 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}
Ответ 5
Для полноты полноты также есть clj-json, который использует Джексона под синтаксический разбор JSON.
Ответ 6
С версией 0.1.2 из clojure.data.json все это могло бы выглядеть следующим образом:
(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\"}"`