четверг, 10 июня 2010 г.

cl-json и plists

Сначала несколько слов о JSON и Common Lisp.

Есть достаточно распространённое мнение, что CL хорошо подходит для генерации всяких XML, HTML и т.п., и для решения подобных задач предлагается множество решений на базе s-выражений. Из своего опыта могу заключить, что подобное мнения однозначно является заблуждением и для генерации XML/HTML лучше всего подходят шаблоны, но в этом случае CL не имеет специфичных преимуществ над другими языками (ну кроме тех, что CL просто лучший язык).

А вот JSON действительно очень хорошо подходит для использования с Common Lisp, ибо может непосредственно отображаться на списки, которые являются основной структурой данных в CL. Поскольку JSON также непосредственно отображается и на хэш-таблицы, которые являются основным типов данных для JavaScript (можно утверждать, что хэш-таблицы в JavaScript имеют такое же ключевое значение, как списки в CL), то связка CL<=>JavaScript с взаимодействием на основе JSON вообще смотрится очень выигрышной: и там и там без каких-либо дополнительных усилий можно непосредственно работать с наиболее естественной формой представления данных.

В какой-то момент для работы с JSON в Common Lisp я выбрал библиотеку cl-json, уже точно не помню, что мной двигало, но наверное то, что это простое наиболее известное решение. Сейчас я подумываю о том, что бы изменить это решение, ибо авторы cl-json кажется уж очень увлеклись своими концепциями. Но не суть. Так вот, в этой библиотеке для представления объектов/хэшов используются alists, которые я считаю не удобными для использования человеком и предпочитаю строить свои решения на базе plists. Соответственно, имеющаяся у меня инфраструктура гораздо более дружественна к plists и я захотел переоренитровать cl-json на работу с plists. Добился я этого следующим кодом, который поясняет моё отношение к данной библиотеке - он хотя и довольно краток, но его нельзя написать без изучения исходников библиотеки:
(defun encode-json (obj)
(flet ((encode-json-list (list stream)
(if (keywordp (car list))
(json:encode-json-plist list stream)
(json::encode-json-list-guessing-encoder list stream))))
(let ((json::*json-list-encoder-fn* #'encode-json-list))
(json:encode-json-to-string obj))))

(defun decode-json (str)
(flet ((accumulator-add-pkey (key)
(json::accumulator-add (funcall json:*identifier-name-to-key*
(funcall json:*json-identifier-name-to-lisp* key))))
(accumulator-add-pvalue (value)
(json::accumulator-add value)))
(let ((json:*object-key-handler* #'accumulator-add-pkey)
(json:*object-value-handler* #'accumulator-add-pvalue))
(json:decode-json-from-string str))))


CL-USER> (encode-json '(:a 1 :b (:c 2 :d 3) :e 4))
"{\"a\":1,\"b\":{\"c\":2,\"d\":3},\"e\":4}"
CL-USER> (decode-json (encode-json '(:a 1 :b (:c 2 :d 3) :e 4)))
(:A 1 :B (:C 2 :D 3) :E 4)
Да, я использую darcs-версию библиотеки, которая содержит важное изменение, почему-то не оформленное в виде какого-либо релиза.

4 комментария:

  1. Кстати, а почему XML не непосредственно отображается на s-выражения?

    ОтветитьУдалить
  2. @quasimoto

    Потому что есть атрибуты, пространства имён, CDATA и т.п. XML это сложный язык разметки, не формат данных, а JSON это чистые данные.

    ОтветитьУдалить
  3. Symbol "*IDENTIFIER-NAME-TO-KEY*" not found in the JSON package. [Condition of type SB-INT:SIMPLE-READER-PACKAGE-ERROR]

    cl-json_0.4.0

    ОтветитьУдалить
  4. @Rigidus

    Ну я же написал, что использую версию из darcs ;) Там были небольшие, но важные изменения, не оформленные в виде релиза. В виде архива можно скачать отсюда: http://github.com/downloads/archimag/archimag-lisp-overlay/cl-json-0.4.0_p20100610.tar.bz2

    ОтветитьУдалить