Подтвердить что ты не робот

Как сопоставить код 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
4b9b3361

Ответ 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\"}"`