trivial-json-codec

2019-03-07

Trivial JSON Codec Manual

[in package TRIVIAL-JSON-CODEC]

pipeline status Quicklisp

Description

A JSON parser able to handle class hierarchies.

Limitations: It expects the classes of the hierarchy to have at least one slot named differently. It does not fully follow the JSON specification.

Lists are serialized different than arrays:

(trivial-json-codec:serialize-json '(1 2 3 4))
=> "<1,2,3,4>"

(trivial-json-codec:serialize-json (make-array (1) :initial-contents '(1 2 3 4)))
=> "[1,2,3,4]"

Installing trivial-json-codec

This project is available in the latest QuickLisp distribution, so installing it is reduced to calling:

(ql:quickload :trivial-json-codec)

Working Example

The following code demonstrates the use-case for this library.

Note: The sequence '=>' indicates the result from evaluating the previous expression.

(defclass Payload ()
  ())

(defclass SimplePayload (Payload)
  ((value :type integer
          :initarg :value)))

(defclass ComplicatedPayload (Payload)
  ((value :type string
          :initarg :value)
   (additional-info :type string
            :initarg :additional-info)
   (message-id :type trivial-utilities:positive-fixnum
           :initarg :message-id)))

(defclass DifferentPayload (Payload)
  ((cargo :type fixnum
          :initarg :cargo)))

(defclass Message ()
  ((uid :initarg :uid
    :initform nil
    :accessor uid)
   (payload :type (or null Payload)
        :initarg :payload
        :accessor payload)))

(c2mop:ensure-finalized (find-class 'Payload))
(c2mop:ensure-finalized (find-class 'SimplePayload))
(c2mop:ensure-finalized (find-class 'ComplicatedPayload))
(c2mop:ensure-finalized (find-class 'DifferentPayload))
(c2mop:ensure-finalized (find-class 'Message))

(let ((message (make-instance 'Message :uid 1 :payload (make-instance 'Simplepayload :value 12345))))
  (trivial-json-codec:serialize-json message))
=> "{ \"UID\" : 1,  \"PAYLOAD\" : { \"VALUE\" : 12345}}"

(deserialize-json "{ \"UID\" : 1,  \"PAYLOAD\" : { \"VALUE\" : 12345}}" :class (find-class 'Message))
=> #<MESSAGE> with a payload of type SimplePayload


(let ((message (make-instance 'Message :uid 2 :payload (make-instance 'ComplicatedPayload :value "abc" :message-id 17 :additional-info "1234"))))
  (trivial-json-codec:serialize-json message))
=> "{ \"UID\" : 2,  \"PAYLOAD\" : { \"VALUE\" : \"abc\",  \"ADDITIONAL-INFO\" : \"1234\",  \"MESSAGE-ID\" : 17}}"

(deserialize-json "{ \"UID\" : 2,  \"PAYLOAD\" : { \"VALUE\" : \"abc\",  \"ADDITIONAL-INFO\" : \"1234\",  \"MESSAGE-ID\" : 17}}" :class (find-class 'Message))
=> #<MESSAGE> with a payload of type ComplicatedPayload

(let ((message (make-instance 'Message :uid 2 :payload (make-instance 'DifferentPayload :cargo -147))))
  (trivial-json-codec:serialize-json message))
=> "{ \"UID\" : 2,  \"PAYLOAD\" : { \"CARGO\" : -147}}"

(deserialize-json "{ \"UID\" : 2,  \"PAYLOAD\" : { \"CARGO\" : -147}}" :class (find-class 'Message))
=>  #<MESSAGE> with a payload of type DifferentPayload


Due to the known limitation mentioned in the description, the following is NOT possible:

(defclass StringPayload (Payload)
  ((value :type string
          :initarg :value)))

(let ((message (make-instance 'Message :uid 2 :payload (make-instance 'StringPayload :value "abc"))))
  (trivial-json-codec:serialize-json message))
=> "{ \"UID\" : 2,  \"PAYLOAD\" : { \"VALUE" : \"abc\"}}"

(deserialize-json "{ \"UID\" : 2,  \"PAYLOAD\" : { \"VALUE" : \"abc\"}}" :class (find-class 'Message))
=> This terminates with an error due to non-unique class mapping. StringPayload and Simplepayload differ only on the slot's type.

Exported Symbols

  • [function] SERIALIZE-JSON OBJ

    Takes OBJ and serializes it into a string. Uses the generic SERIALIZE to do the job.

  • [function] DESERIALIZE-JSON JSON-STR &KEY (CLASS NIL) (READ-TABLE NIL)

    Reads JSON-STR and creates an according object. If CLASS is non-nil and represents a class, an instance of it is returned. Otherwise only built-in types can be deserialized. READ-TABLE makes it possible to inject specific readers, as pondons to SERIALIZE. It has the form of an alist containing the dispatch character as car and the deserialization function as cdr.

  • [generic-function] SERIALIZE OBJ STREAM

    Serialize an object OBJ into STREAM. Implementations for built-in types already exist. The user might extend with methods for specific types.

License Information

This library is released under the MIT License. Please refer to the LICENSE to get the full licensing text.

Contributing to this project

Please refer to the CONTRIBUTING document for more information.