Liking cljdoc? Tell your friends :D

Getting Started

jason is a thin wrapper around jsonista, a jackson based JSON encoding and decoding library, providing sensible default but fully configurable key conversion facilities.

jackson utilises an ObjectMapper instance for all JSON serialisation and deserialisation. ObjectMapper instances are expensive to create and should be reused as much as possible. As such, jason aims to minimise the number of ObjectMapper instances required.

Building coder functions

To build an encoder and decoder:

(require '[jason.core :as jason])

(def default-object-mapper (jason/new-object-mapper))
(def ->default-json (jason/new-json-encoder default-object-mapper))
(def <-default-json (jason/new-json-decoder default-object-mapper))

(->default-json {:first-name "Jane"})
;; => "{\n  \"firstName\" : \"Jane\"\n}"

(<-default-json "{\n  \"firstName\" : \"Jane\"\n}")
;; => {:first-name "Jane"}

As shown, by default, the encoder will convert keys to camel case strings. The decoder will convert keys to kebab case keywords.

Basic configuration

To change the conversions:

(require '[camel-snake-kebab.core 
           :refer [->snake_case_string
                   ->snake_case_keyword]])

(def custom-object-mapper (jason/new-object-mapper
                            {:encode-key-fn 
                             (jason/->encode-key-fn ->snake_case_string)
                             :decode-key-fn
                             (jason/->decode-key-fn ->snake_case_keyword)}))
(def ->custom-json (jason/new-json-encoder custom-object-mapper))
(def <-custom-json (jason/new-json-decoder custom-object-mapper))

(->custom-json {:first-name "Jane"})
;; => "{\n  \"first_name\" : \"Jane\"\n}"

(<-custom-json "{\n  \"firstName\" : \"Jane\"\n}")
;; => {:first_name "Jane"}

Meta handling

In addition to key conversion for standard keys, jason is aware of metadata keys. By default these are keys prefixed with an underscore, e.g., _links. The default key conversion functions and the conversion functions returned by ->encode-key-fn and ->decode-key-fn convert the key whilst retaining the metadata prefix:

(def default-encode-key-fn (jason/->encode-key-fn))
(def default-decode-key-fn (jason/->decode-key-fn))

(default-encode-key-fn :_meta-data)
;; => "_metaData"

(default-decode-key-fn "_metaData")
;; => :_meta-data

(def custom-encode-key-fn (jason/->encode-key-fn ->snake_case_string))
(def custom-decode-key-fn (jason/->decode-key-fn ->snake_case_keyword))

(custom-encode-key-fn :_meta-data)
;; => "_meta_data"

(custom-decode-key-fn "_metaData")
;; => :_meta_data

To fully override the metadata handling:

(def heterogeneous-encode-key-fn (jason/->encode-key-fn 
                                   {:standard-key-fn ->snake_case_string
                                    :meta-key-fn name}))
(def heterogeneous-decode-key-fn (jason/->decode-key-fn
                                   {:standard-key-fn ->snake_case_keyword
                                    :meta-key-fn keyword}))

(heterogeneous-encode-key-fn :_meta-data)
;; => "_meta-data"
(heterogeneous-encode-key-fn :other-field)
;; => "other_field"

(heterogeneous-decode-key-fn "_metaData")
;; => :_metaData
(heterogeneous-decode-key-fn "otherField")
;; => :other_field

Additional configuration

In addition to key function configuration, [[jason.core/new-object-mapper]] exposes all configuration options of the underlying jsonista library.

See [[jason.core/new-object-mapper]] for more details.

Defining coder functions using new-json-coders and defcoders

The [[jason.core/new-json-coders]] function builds both coders at once and returns them in a map, hiding the details of the ObjectMapper:

(let [{:keys [->json <-json]} 
      (jason/new-json-coders)]
  (->json {:first-name "Jess"})
  ;; => "{\"firstName\": \"Jess\"}"

  (<-json "{\"lastName\": \"Jacobs\"}")
  ;; => {:last-name "Jacobs"}
  )

(let [{:keys [->json <-json]} 
      (jason/new-json-coders
        {:encode-key-fn (jason/->encode-key-fn ->snake_case_string)
         :decode-key-fn (jason/->decode-key-fn ->snake_case_keyword)})]
  (->json {:first-name "Jane"})
  ;; => "{\n  \"first_name\" : \"Jane\"\n}"
  
  (<-json "{\n  \"firstName\" : \"Jane\"\n}")
  ;; => {:first_name "Jane"}
  )

Usually, you'll want to hang on to the resulting coders for later use. This can be achieved with:

(let [{:keys [->json <-json]} 
      (jason/new-json-coders
        {:encode-key-fn (jason/->encode-key-fn ->snake_case_string)
         :decode-key-fn (jason/->decode-key-fn ->snake_case_keyword)})]
  (def ->db-json ->json)
  (def <-db-json <-json))

However, for convenience, the defcoders macro performs this task for you:

(require '[jason.core :refer [defcoders]])

(defcoders db
  :encode-key-fn (jason/->encode-key-fn ->snake_case_string)
  :decode-key-fn (jason/->decode-key-fn ->snake_case_keyword))

(->db-json {:first-name "Jane"})
;; => "{\n  \"first_name\" : \"Jane\"\n}"
  
(<-db-json "{\n  \"firstName\" : \"Jane\"\n}")
;; => {:first_name "Jane"}

All options that can be passed to new-json-coders or new-object-mapper also apply to defcoders.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close