如何使用 class-json 库将 json-string 转换为 CLASS 对象?

How to convert json-string into CLOS object using cl-json library?

如果有一个class和一个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]

我做错了什么?

错误的原因是 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 数据中的原型键

现在,您也可以定义自己的 class:

(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\"  }}")))

上面的returns是FOO的一个实例。

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

强制使用默认原型(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)))))

上面为给定的 class(符号)创建了一个原型对象,捕获了 *end-of-object-handler* 的当前绑定,并且 returns 一个闭包,当被调用时,绑定 *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 的实例。

递归

注意如果定义foo如下:

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

然后黑客还将嵌套的 JSON 对象读取为 FOO:

(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\" : { \"bar\" : 10} }"))))

=> #<FOO {1007A70E23}>

> (describe *)
#<FOO {1007A70E23}>
  [standard-object]

Slots with :INSTANCE allocation:
  BAR                            = 3
  FOO                            = #<FOO {1007A70D53}>

> (describe (foo **))
#<FOO {1007A70D53}>
  [standard-object]

 Slots with :INSTANCE allocation:
  BAR                            = 10
  FOO                            = #<unbound slot>