如何使用 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>
如果有一个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>