I have added a new alternative encoder to the cl-json darcs repo. I
call it the "explicit" (sexp) encoder and now call the old/other
(sexp) decoder the "guessing" encoder. The old "guessing" encoder is
still the default, so this should not break any code. Also there is a
third "streaming" encoder. In other words, flexibility.
The "streaming" encoder is a way to format json to a stream. This
gives you detailed control, but some of us prefer an intermediate
s-exp format.
The old "guessing" encoder tries to make a reasonable json
representation of a lisp s-expression. For example a list becomes a
json array. An a-list becomes a json object. An empty list becomes an
empty json array. But some might want it to be json null. Or json
false. Or an empty json literal object.
With the "explicit" encoder, you give directives in the s-expression
explaining exactly what json you want. This might give you more
control. Also new is that you can use p-lists to output objects. Also,
you can include a pre-formated json string if you wish.
I have not updated the documentation, because it was autogenerated by
Boris Smilga, and I don't know how to do it. Boris, can you help with
this or the code to your doc-maker?
There are some testcases and doc-strings explaining the use in the
code, I selected one of each and put below in the mail.
Also, I have found a bug with json-bind (not fixed, but I have added a
testcase for it in case someone want to give it a try).
Note that there is no corresponding decoder yet, simply because I have
not had any egoistic reasons to add it. But it should be easy if
someone want an exercise.
Hope someone has a use for this,
Henrik
"Write the JSON representation of the list S to STREAM (or to
*JSON-OUTPUT*), using one of the two rules specified by
first calling USE-GUESSING-ENCODER or USE-EXPLICIT-ENCODER.
The guessing encoder: If S is a list encode S as a JSON Array, if
S is a dotted list encode it as an Object (per ENCODE-JSON-ALIST).
The explicit decoder: If S is a list, the first symbol defines
the encoding:
If (car S) is 'TRUE return a JSON true value.
If (car S) is 'FALSE return a JSON false value.
If (car S) is 'NULL return a JSON null value.
If (car S) is 'JSON princ the strings in (cdr s) to stream
If (car S) is 'LIST or 'ARRAY encode (cdr S) as a a JSON Array.
If (car S) is 'OBJECT encode (cdr S) as A JSON Object,
interpreting (cdr S) either as an A-LIST or a P-LIST."
(test explicit-encoder-complex-objects
(let ((sample-1-alists
`(:object
(:method . some-function)
(:id . 1)
(:params .
(:list
(:object (:id . bar-id)
(:name . bar))
(:true)
(:list (:object
(:name . a)
(:id . b)))
(:list (:object
(:name . foo)
(:id . foo-id)))
))))
(sample-2-plists
`(:object
:method some-function
:id 1
:params (:list
(:json "{\"id\":\"barId\",\"name\":\"bar\"}")
(:true)
(:list (:object "name" a :id b))
(:list (:object
:name foo
:id foo-id))
)))
(correct-json "
{\"method\":\"someFunction\",
\"id\":1,\"params\":
[{\"id\":\"barId\",\"name\":\"bar\"},
true,
[{\"name\":\"a\",\"id\":\"b\"}],
[{\"name\":\"foo\",\"id\":\"fooId\"}]]}")
exact-json sample-1-json sample-2-json)
(setf sample-1-json
(with-explicit-encoder
(encode-json-to-string sample-1-alists)))
(setf sample-2-json
(with-explicit-encoder
(encode-json-to-string sample-2-plists)))
(is (string= sample-1-json sample-2-json))
(setf exact-json (remove #\Newline
(remove #\Space correct-json :test #'char=)
:test #'char=))
(is (string= sample-1-json exact-json))))