众所周知JSON是JavaScript的数据交换格式——简单但却封闭且不可靠。

例如JSON并未定义时间格式。没有统一的标准使得交换不再可靠。你说2014/1/5是一月五号还是五月一号?还有虽然JSON“定义”了数字很像C或Java的number,但实际实现的只有JavaScript的浮点数。这样C或Java的两个不同的整数,例如 9007199254740992 和 9007199254740993 使用JSON交换后竟然相等。

JSON只有七种格式:真假空数句表格。其中真true假false空null三个代表就占了七席中的约一半,可它们除了自己什么都不代表。数number可以是不大不小的整数或者不一定准的科学计数。句string是一串只用一对双引号夹住的统一码Unicode、但又没有明确是统一在UTF8/16/32还是其他的标准下。表object用于名值对照,例如写成{"name":{"true":null,"false":true}}用来代表一个有名真空假亦真的对象。格array也是表,只是不称名字叫号码,[3.14159,2.71828,1.618]三个号子关的分别是圆周率π欧拉数e和黄金分割φ的近似值。

尽管JSON是JavaScript的对象,但JavaScript的对象不一定是JSON。不符合条件的一大堆,比如名字是单引号的不行,没有引号的也不行;比如JSON必须是表格,单独的真假空数句统统不行。最大的局限在于,JSON对如何规定统一的规定没有任何的规定,这就必须靠交换双方私下里有些协议,例如双方同意的交换必须包括sex而不能叫gender,名字必须是UUID而不能具实名等等。如果能够在JSON格式中把这些潜规则明确下来,例如用{"protocol":"GB/1984","sex":"you","name":"me"},则双方都会知道这是个违规的交易。

我在使用中发现,其实有三个未加说明的特性能极大提高JSON的性能。

第一,词 symbol,不需要用引号标注也不必是字母数字。例如 i,=,you,? 是四个用逗号分隔的词。

第二,贴 tag,是#开头的词,用来定义规则。例如 #GB/1984 是上面交易双方自己的规则;#inst 是符合RFC-3339的一个时间点,例如 #inst "1970-10-04-T01:02:03.04Z"; #uuid 是8-4-4-4-12格式的32个随机16进制通用唯一码。

第三,例 list。除了使用花括号方括号的表格,我们还可以用圆括号括起一系列的词。例如 (print,3)。括号里的第一个位置是方式function,作用于后面列出的其他数值。除了内部的一些方式,我们也可以使用 defn 定义自己的方式。例如 (defn,forever,[],(forever)),这样我们就多了一种方式可以完成堆栈溢出StackOverflow。如果逗号太扎眼,我们完全可以为之省略。

我们可以使用 if 加以判断,并分别规定真假用例。例如 (if nil true false) 是假的,因为 if 把所有不是 nil 不是 false 的都当真。这样我们就可以写些有用的方式,例如计算 n! 的公式:

(defn factorial [n] (if (= n 1) 1 (* n (factorial (- n 1)))

天,这不是 LISP 是什么?!

(注,本文启发自http://www.jayway.com/2013/04/01/three-undocumented-features-of-json-3/)

(又注,这不是 LISP,是借 JSON 引入 Clojure 和 EDN。第12期码农的97页也有介绍。)