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

Boris Smilga boris.smilga at gmail.com
Sun Aug 14 22:29:13 UTC 2011


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.





More information about the cl-json-devel mailing list