[cl-json-devel] Guidance requested: how to dispatch members to external decoders?

Hraban Luyat hraban at 0brg.net
Mon Aug 15 19:20:09 UTC 2011


Aha! :internal-decoder! Perfect, exactly what I need. Thank you very
much for your extensive explanation.

Also, I will change the spec to send the data as an array instead of
an object to enforce order.

Thanks again.

Sincerely,

Hraban Luyat

2011/8/15 Boris Smilga <boris.smilga at gmail.com>:
> On 14 Aug 2011, at 16:24, Hraban Luyat wrote:
>
>> The semantics of my incoming messages are thus:
>>
>> object:
>>  - "type": string denoting the type
>>  - "payload": type-specific payload
>>
>> I want to create a decoder that only extracts the type and uses that
>> to determine which decoder to send the payload to. Then it continues
>> with whatever lisp object the decoder returned.
>>
>> What I thought would be appropriate is to create a generic function;
>>
>> (defgeneric json->data (type payload))
>>
>> and then simply register decoders as follows:
>>
>> (defmethod json->data ((type (eq :foo)) payload)
>>  "Decode message of type foo."
>>  ...)
>>
>> (defmethod json->data ((type (eq :bar)) payload)
>>  "Decode message of type bar."
>>  ...)
>>
>> But now I am not really sure how to glue this together. What would you
>> recommend? Is this the right frame of mind at all or should I take a
>> totally different approach?
>
> Why, yes, you can certainly make it that way.
>
> Re. gluing it together: there is some relevant reading in CL-JSON User Guide
> under http://common-lisp.net/project/cl-json/#DECODER-CUSTOMIZATION .  To
> apply the API to your case, you'd have to define a dynamic variable which
> would store the type while the decoder is waiting for the payload to arrive,
> and another, which would store the payload while the decoder is waiting for
> the end of the object:
>
>  (defvar *payload-type* nil)
>
>  (defvar *payload* nil)
>
> Then you customize your decoder to decode top-level JSON {} objects in the
> following way:
>
>  (let ((default-decoder (json:current-decoder)))
>    (json:bind-custom-vars
>        (;; Initialize variables in the dynamic scope of the object:
>         :beginning-of-object
>          (lambda () (setq *payload-type* nil *payload* nil))
>         ;; The handler for key sets the internal decoder and the
>         ;; handler for value depending on the key:
>         :object-key
>          (lambda (json-id)
>            (let ((lisp-id
>                   (json:safe-json-intern
>                     (funcall json:*json-identifier-name-to-lisp*
>                              json-id))))
>              (case lisp-id
>                ;; If the key is "type", the value is decoded in the
>                ;; standard way and stored in *payload-type*:
>                ((:type)
>                 (json:set-custom-vars
>                   :internal-decoder
>                    default-decoder
>                   :object-value
>                    (lambda (value) (setq *payload-type* value))))
>                ;; If the key is "payload", the value is decoded by
>                ;; your type-specific decoder and stored in *payload*:
>                ((:payload)
>                 (json:set-custom-vars
>                   :internal-decoder
>                    (lambda (stream)
>                      (json->data *payload-type* stream))
>                   :object-value
>                    (lambda (value) (setq *payload* value)))))))
>         ;; The value of the whole object is the value decoded from
>         ;; its "payload" field:
>         :end-of-object
>          (lambda () *payload*)
>         ;; This you need only if your type+payload objects may be
>         ;; nested:
>         :object-scope
>          '(*payload* *payload-type*))
>      (decode-json)))
>
> For clarity, I've left out any safety checks, which you might want to put in
> here or there.  Also, this code includes a tacit assumption that, in your
> objects, the "type" field always precedes the "payload" field.  If you
> cannot guarantee this, you'll have to provide a json->data method for type
> NIL which maybe decodes to some intermediate representation, and add code to
> the value handler for "type" fields to finalize such semi-parsed values if
> found in *payload*.
>
> Hope this helps; feel free to ask me for clarifications if you find some
> portion of the code to be too obscure.
>
> Sincerely,
>  - B. Smilga.
>
>
> _______________________________________________
> cl-json-devel mailing list
> cl-json-devel at common-lisp.net
> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/cl-json-devel
>




More information about the cl-json-devel mailing list