Common Lisp使用CL-JSON将alist转JSON报错的原因与解决方法
问题原因
报错的核心原因是传入json:encode-json-alist-to-string的参数根本不是预期的alist结构:
- 你定义的用户输入字符串最前面带了Lisp的引用语法糖单引号
',read-from-string读取这个字符串时,会把单引号解析为quote符号,最终读出来的结果是(quote (("name" . "John")))——这是一个首元素为符号QUOTE、第二个元素才是目标alist的列表,而非alist本身。 - 你直接在REPL里手写
'(("name" . "Pedro"))能正常运行,是因为Lisp求值器会自动处理quote表达式,返回引号包裹的实际alist值;但read-from-string只做语法读取、不做求值,所以你拿到的返回值是未求值的quote表达式,自然不符合CL-JSON对alist参数的类型要求。
你可以自行验证读入结果的结构:
CL-USER> (car (read-from-string input-from-user)) QUOTE
解决方案
根据场景选以下任意一种方案即可:
- 方案一(最推荐,无安全风险):从输入源头去掉前置单引号,让
read-from-string直接读出alist结构;; 输入字符串去掉最开头的' CL-USER> (defvar input-from-user "((\"name\" . \"John\"))") INPUT-FROM-USER CL-USER> (json:encode-json-alist-to-string (read-from-string input-from-user)) "{\"name\":\"John\"}" - 方案二(适合无法修改用户输入规则的场景,无安全风险):读取后手动取出
quote包裹的实际alist值,也就是读入结果的第二个元素,代码做兼容判断可以同时支持带引号和不带引号的输入CL-USER> (defvar input-from-user "'((\"name\" . \"John\"))") INPUT-FROM-USER CL-USER> (let ((parsed (read-from-string input-from-user))) (json:encode-json-alist-to-string (if (eq (car parsed) 'quote) (cadr parsed) parsed))) "{\"name\":\"John\"}" - 方案三(仅适合完全可信的输入场景,有安全风险):对读入的结果直接求值
注意:如果用户输入不可信,绝对不要用这个方案,恶意构造的输入会被直接执行,带来严重安全问题。
CL-USER> (json:encode-json-alist-to-string (eval (read-from-string input-from-user))) "{\"name\":\"John\"}"
内容的提问来源于stack exchange,提问作者Pedro Delfino
相关产品推荐
相关产品推荐

