Как отобразить код clojure в JSON и из него? - PullRequest
22 голосов
/ 09 августа 2010

У меня есть сумасшедшая идея, которая включает в себя помещение некоторого кода clojure в CouchDB и написание представлений, запрашивающих его. Я не хочу хранить код clojure в виде простого текста, потому что тогда мне придется беспокоиться о его разборе в представлениях. Форматирование и комментарии не нужно сохранять, но код должен иметь возможность входить и выходить из базы данных без изменения структуры. Ключевые слова, символы и строки должны оставаться в своем родном типе. Кроме того, я хочу, чтобы код выглядел элегантно и был эффективным.

Я думаю представить вещи следующим образом:

  • Символы в виде строк, начинающиеся с '
  • Ключевые слова в виде строк, начинающихся с:
  • Строки не изменены, за исключением случаев, когда они начинаются с 'или: в этом случае они экранируются обратной косой чертой.
  • (parens) в виде массива
  • [скобки] в виде массива с "_ []" в качестве первого элемента
  • отображает ({}) как объект
  • устанавливает (# {}) в качестве объекта со значениями, установленными в 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

Ответы [ 6 ]

9 голосов
/ 09 августа 2010

Как и предположил Микера, clojure.contrib.json / write-json преобразует не только примитивные типы, но и ISeq s Clojure и Map s, Collection s и Array в Java. с тоже. Это должно охватывать большую часть вашего кода (рассматриваемого как данные), но в случае, если вы хотите написать что-то более изощренное, легко расширить JSON Writer, имитируя исходный код Стюарта Сьерры (см. здесь ):

(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-выражениями , у вас должно быть все в порядке.

Разбор JSON обратно к данным можно выполнить с помощью clojure.contrib.json/read-json (потребуется немного времени, чтобы посмотреть параметры его определения, возможно, вы захотите их использовать). После этого eval может быть вашим лучшим другом.

8 голосов
/ 09 августа 2010

Если вы хотите использовать JSON в качестве представления, я настоятельно рекомендую использовать clojure.contrib.json , который уже выполняет работу по преобразованию структур данных Clojure в JSON довольно легко.

Нет смысла изобретать велосипед: -)

Я довольно успешно использовал его в своем текущем проекте Clojure.Если он не делает все, что вы хотите, то вы всегда можете добавить патч для его улучшения!

7 голосов
/ 31 июля 2012

Я думаю, что ваша идея обоснована, но я бы упростил обработку коллекций, используя вместо этого помеченные массивы (["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 оставлено в качестве упражнения для читателя. ;)

5 голосов
/ 27 января 2014

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}
4 голосов
/ 09 августа 2010

Для полноты картины есть также clj-json , который использует Джексона для анализа JSON.

0 голосов
/ 18 марта 2012

С версией 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\"}"`
...