如题, 在使用 emacs-jupyter 的时候发现了一个报错, 调试了半天发现是代码里有处涉及到 json-encode 一个 cl-defstruct 出来的值, 会报错
简单的尝试了一下
(cl-defstruct foo (f1 “foo”)) (json-encode (make-foo))
发现确实不行, 报错,
(json-error #s(foo :f1 “foo”))
这是因为 json.el 确实缺少了相关支持吗, 还是我使用上哪里有问题
如题, 在使用 emacs-jupyter 的时候发现了一个报错, 调试了半天发现是代码里有处涉及到 json-encode 一个 cl-defstruct 出来的值, 会报错
简单的尝试了一下
(cl-defstruct foo (f1 “foo”)) (json-encode (make-foo))
发现确实不行, 报错,
(json-error #s(foo :f1 “foo”))
这是因为 json.el 确实缺少了相关支持吗, 还是我使用上哪里有问题
第一时间应该看文档。
文档明确了 json-encode
接受的对象:
(json-encode OBJECT) Return a JSON representation of OBJECT as a string. OBJECT should have a structure like one returned by ‘json-read’.
(json-read) Parse and return the JSON object following point. Advances point just past JSON object. If called with the following JSON after point {"a": [1, 2, {"c": false}], "b": "foo"} you will get the following structure returned: ((a . [1 2 ((c . :json-false))]) (b . "foo"))
帖子里的代码应该放在 ```{lang}
和 ```
中
安装 xuchunyang 大佬的 elisp-demos,然后你在看文档的同时也能看到使用范例。
现在 29 添加了 shortdoc,两个都放到 help 中会重复吗
可以看看函数 json--print
,里面有支持的类别:
(defun json--print (object)
"Like `json-encode', but insert or print the JSON at point."
(cond ((json--print-keyword object))
((listp object) (json--print-list object))
((json--print-stringlike object))
((numberp object) (prin1 object))
((arrayp object) (json--print-array object))
((hash-table-p object) (json--print-unordered-map object))
((signal 'json-error (list object)))))
感谢大家的回答, 我更震惊的是 json.el 出于什么样的理由不支持 record 呢, 感觉难以置信
我猜可能是这样,record/struct 需要知道它的结构才有意义,而它的实例只保留了各个字段的 value,所以没法转成 key/value 形式。
#+BEGIN_SRC elisp
(cl-defstruct (person (:constructor person-create)
(:copier nil))
name age sex)
(person-create :name "Tom" :age 20 :sex "Male")
#+END_SRC
#+RESULTS:
: #s(person "Tom" 20 "Male")
当然要转也不是不可能,只要数据能读取就能转:
转成key/value 形式,
给 json-encode 增加一个字段描述的参数:
(json-encode tom (cl-struct-slot-info 'person))
或者实现一个 struct-to-alist
还更靠谱:
(json-encode (struct-to-alist tom 'person)
当作 list 对待。先把 record 转成 list,然后转 json。
record-to-list
(defun record-to-list (record)
"Convert RECORD to list."
(with-temp-buffer
(prin1 record (current-buffer))
(goto-char (point-min))
(when (and (re-search-forward "#s" nil t) (= (char-after) ?\())
(read (current-buffer)))))
(record-to-list (person-create :name "Tom" :age 20 :sex "Male"))
;; => (person "Tom" 20 "Male")
struct-to-alist
(defun struct-to-alist (instance)
"Convert struct to alist."
(let ((lst (record-to-list instance)))
(cl-loop for slot in (cdr (cl-struct-slot-info (car lst)))
for value in (cdr lst)
collect (cons (car slot) value))))
(struct-to-alist (person-create :name "Tom" :age 20 :sex "Male"))
;; => ((name . "Tom") (age . 20) (sex . "Male"))
EDIT: 去掉 struct-to-alist
的 struct-type
参数。
我糊涂了。根本没必要破坏 json-encode
接口,struct 对象本身就包含 struct-type
:
(person-create :name "Tom" :age 20 :sex "Male")
;; => #s(person "Tom" 20 "Male")
;; ^^^^^^
所以只要修改 json--print
,然后实现 json--print-struct
和 json--print-record
就可以了:
+ (defun json--print-struct (object) ...)
+ (defun json--print-redord (object) ...)
(defun json--print (object)
"Like `json-encode', but insert or print the JSON at point."
(cond ((json--print-keyword object))
((listp object) (json--print-list object))
((json--print-stringlike object))
((numberp object) (prin1 object))
((arrayp object) (json--print-array object))
((hash-table-p object) (json--print-unordered-map object))
+ ((cl-struct-p object) (json--print-struct object))
+ ((and (recordp object) (not (cl-struct-p object))) (json--print-record object))
((signal 'json-error (list object)))))
当然 #7 楼的两个转换函数已经够用了,不修改 json.el
亦无大碍。