Макрос разрушения для объекта ST-JSON JSO
У меня есть еще один вопрос, касающийся декодирования JSON в Common Lisp. Я остановился на ST-JSON
как мой инструмент. Я могу получить JSO
объект, содержащий данные JSON, и доступ ко всем полям с помощью st-json:getjso
, Я хотел написать макрос, похожий в принципе на destructuring-bind
это обеспечило бы локальные привязки к переменным, названным в честь полей JSON (с тех пор я начал сомневаться, хорошая ли это идея, но это другой вопрос). Я придумал следующее:
(defmacro destructure-jso (jso &body body)
(let (keys values)
(st-json:mapjso #'(lambda (key value)
(push key keys)
(push value values))
jso)
`(destructuring-bind ,keys ,values
,@body)))
Но когда я пытаюсь использовать его на JSO
объект, я получаю ошибку The value PARAMS is not of the expected type STRUCTURE.
где PARAMS
это объект. Может кто-то объяснить это мне?
Благодарю.
1 ответ
Видимо, вы используете destructure-jso
как это:
(let ((params (st-json:read-json-from-string "{\"foo\":42,\"bar\":\"baz\"}")))
(destructure-jso params
(list foo bar)))
Тем не мение, destructure-jso
будучи макросом, обрабатывается во время раскрытия макроса задолго до того, как анализируется JSON. params
передается вашему макросу как символ, без оценки; и даже если бы была предпринята попытка его оценки, он не был бы связан.
Итак, если вы хотите написать destructure-jso
вам понадобится список ключей во время расширения макроса. Вы можете передать список обычным способом:
> (defmacro destructure-jso-2 (vars json &body body)
`(let ,(mapcar #'(lambda (var)
(list var `(getjso ,(string-downcase (symbol-name var)) ,json)))
vars)
,@body))
DESTRUCTURE-JSO-2
> (let ((params (st-json:read-json-from-string "{\"foo\":42,\"bar\":\"baz\"}")))
(destructure-jso-2 (foo bar)
params
(list foo bar)))
(42 "baz")
Или, если хотите, используйте "шаблонный" JSON для создания отображений:
> (defmacro destructure-jso-3 (template json &body body)
(let (bindings)
(st-json:mapjso #'(lambda (key val)
(declare (ignore val))
(push (list (intern (string-upcase key)) `(getjso ,key ,json))
bindings))
(st-json:read-json-from-string template))
`(let ,bindings
,@body)))
DESTRUCTURE-JSO-3
> (let ((params (st-json:read-json-from-string "{\"foo\":42,\"bar\":\"baz\"}")))
(destructure-jso-3 "{\"foo\":null,\"bar\":null}"
params
(list foo bar)))
(42 "baz")
Здесь привязки переменных происходят из первого (шаблонного) JSON, значения из второго. Шаблон JSON анализируется во время макроразвлечения, params
JSON каждый раз, когда ваш код выполняется.
Является ли один из этих подходов полезным для вас или нет, я не знаю.