如果有一个类和一个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]
我在做什么错了?
答案 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
。
现在,您还可以定义自己的类:
(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*
不同的密钥。
在不更改现有库代码的情况下,您可以修改它,以更改解码步骤的行为。该代码是围绕特殊变量组织的,这些特殊变量在解析的各个阶段用作回调,因此可以使用您自己的函数包装所需的函数:
(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
的实例。