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.
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.
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"}
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
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.
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