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"})
;; => "{\"first-name\":\"Jane\"}"
(<-default-json "{\"firstName\":\"Jane\"}")
;; => {"firstName" "Jane"}
As shown, by default, the encoder will leave keys untouched during encoding and decoding.
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"})
;; => "{\"first_name\":\"Jane\"}"
(<-custom-json "{\"firstName\":\"Jane\"}")
;; => {: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
.
By default metadata keys are left untouched 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
:encode-key-fn (jason/->encode-key-fn)
:decode-key-fn (jason/->decode-key-fn))]
(->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"})
;; => "{\"first_name\":\"Jane\"}"
(<-json "{\"firstName\":\"Jane\"}")
;; => {: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"})
;; => "{\"first_name\":\"Jane\"}"
(<-db-json "{\"firstName\":\"Jane\"}")
;; => {:first_name "Jane"}
All options that can be passed to new-json-coders
or new-object-mapper
also apply to defcoders
.
jason
includes convenience coder functions for wire JSON (e.g., from service
to service) and database JSON (e.g., for databases that support JSON types).
The wire JSON coders:
org.joda.time
and java.time
typesTo use the wire JSON coders:
(require '[jason.convenience :as conv])
(conv/->wire-json {:first-name "Jane"})
;; => "{\n \"firstName\" : \"Jane\"\n}"
(conv/<-wire-json "{\n \"firstName\" : \"Jane\"\n}")
;; => {:first-name "Jane"}
The database JSON coders:
org.joda.time
and java.time
typesTo use the database JSON coders:
(require '[jason.convenience :as conv])
(conv/->db-json {:first-name "Jane"})
;; => "{\n \"first_name\" : \"Jane\"\n}"
(conv/<-db-json "{\n \"first_name\" : \"Jane\"\n}")
;; => {:first-name "Jane"}
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close