Макрос разрушения для объекта 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 каждый раз, когда ваш код выполняется.

Является ли один из этих подходов полезным для вас или нет, я не знаю.

Другие вопросы по тегам