如何使用cl-json库将json-string转换为CLOS对象?

时间:2019-03-06 05:00:06

标签: json common-lisp clos

如果有一个类和一个json:

(defclass foo ()
    ((bar :initarg :bar)))

(defvar *input* "\{ \"bar\" : 3 }")

如何使用cl-json库将*input*转换为foo的实例?

我想应该是这样的:

(with-decoder-simple-clos-semantics
    (let ((*prototype-name* 'foo))
      (decode-json-from-string *input*)))

但是会产生:

Invalid SB-MOP:SLOT-DEFINITION initialization: the
initialization argument :NAME was constant: :BAR.
   [Condition of type SB-PCL::SLOTD-INITIALIZATION-ERROR]

我在做什么错了?

1 个答案:

答案 0 :(得分:3)

错误的原因是cl-json:*json-symbols-package*绑定到KEYWORD包:当JSON键变成符号时,它们变成显然不是插槽名称的关键字。

流体对象

以下作品:

(let ((json:*json-symbols-package* (find-package :cl-user)))
  (json:with-decoder-simple-clos-semantics
    (json:decode-json-from-string "{ \"bar\" : 3 }")))

(注意:您只需要在双引号字符前加反斜杠)

您获得一个FLUID-OBJECT

JSON数据中的原型键

现在,您还可以定义自己的类:

(in-package :cl-user)
(defclass foo ()
  ((bar :initarg :bar)))

然后,JSON需要具有一个"prototype"密钥:

 (let ((json:*json-symbols-package* (find-package :cl-user)))
   (json:with-decoder-simple-clos-semantics
     (json:decode-json-from-string
      "{ \"bar\" : 3 , 
         \"prototype\" : { \"lispClass\" : \"foo\", 
                           \"lispPackage\" : \"cl-user\"  }}")))

上面的代码返回FOO的实例。

通过重新绑定"prototype",可以使用与*prototype-name*不同的密钥。

强制使用默认原型(hack)

在不更改现有库代码的情况下,您可以修改它,以更改解码步骤的行为。该代码是围绕特殊变量组织的,这些特殊变量在解析的各个阶段用作回调,因此可以使用您自己的函数包装所需的函数:

(defun wrap-for-class (class &optional (fn json::*end-of-object-handler*))
  (let ((prototype (make-instance 'json::prototype :lisp-class class)))
    (lambda ()
      ;; dynamically rebind *prototype* right around calling fn
      (let ((json::*prototype* prototype))
        (funcall fn)))))

上面的代码为给定的类(符号)创建了一个原型对象,捕获了*end-of-object-handler*的当前绑定,并返回了一个闭包,该闭包在被调用时将*prototype*绑定到封闭的原型实例上。

然后,您将其称为:

(let ((json:*json-symbols-package* *package*))
  (json:with-decoder-simple-clos-semantics
    (let ((json::*end-of-object-handler* (wrap-for-class 'foo)))
      (json:decode-json-from-string
       "{ \"bar\" : 3 }"))))

您有一个FOO的实例。