/jsonista

Clojure library for fast JSON encoding and decoding.

Primary LanguageClojureEclipse Public License 2.0EPL-2.0

jsonista Continuous Integration status cljdoc badge

jsonissa / jsonista / jsoniin, jsonilla / jsonilta / jsonille

Clojure library for fast JSON encoding and decoding.

Faster than data.json or Cheshire while still having the necessary features for web development. Designed for use with Muuntaja.

Blogged:

Hi! We are Metosin, a consulting company. These libraries have evolved out of the work we do for our clients. We maintain & develop this project, for you, for free. Issues and pull requests welcome! However, if you want more help using the libraries, or want us to build something as cool for you, consider our commercial support.

Latest version

Clojars Project

Requires Java1.8+

Quickstart

(require '[jsonista.core :as j])

(j/write-value-as-string {"hello" 1})
;; => "{\"hello\":1}"

(j/read-value *1)
;; => {"hello" 1}

Examples

Using explicit ObjectMapper:

(-> {:dog {:name "Teppo"}}
    (j/write-value-as-bytes j/default-object-mapper)
    (j/read-value j/default-object-mapper))
;; => {"dog" {"name" "Teppo"}}

Using keyword keys:

(-> {:dog {:name "Teppo"}}
    (j/write-value-as-bytes j/keyword-keys-object-mapper)
    (j/read-value j/keyword-keys-object-mapper))
;; => {:dog {:name "Teppo"}}

Changing how map keys are encoded & decoded:

(defn reverse-string [s] (apply str (reverse s)))

(def mapper
  (j/object-mapper
    {:encode-key-fn (comp reverse-string name)
     :decode-key-fn (comp keyword reverse-string)}))

(-> {:kikka "kukka"}
    (doto prn)
    (j/write-value-as-string mapper)
    (doto prn)
    (j/read-value mapper)
    (prn))
; {:kikka "kukka"}
; "{\"akkik\":\"kukka\"}"
; {:kikka "kukka"}

Reading & writing directly into a file:

(def file (java.io.File. "hello.json"))

(j/write-value file {"hello" "world"})

(slurp file)
;; => "{\"hello\":\"world\"}"

(j/read-value file)
;; => {"hello" "world"}

Adding support for joda-time Classes, used by clj-time.

;; [com.fasterxml.jackson.datatype/jackson-datatype-joda "2.9.5"]
(import '[com.fasterxml.jackson.datatype.joda JodaModule])
(import '[org.joda.time LocalDate])

(def mapper
  (j/object-mapper
    {:modules [(JodaModule.)]}))

(j/write-value-as-string (LocalDate. 0) mapper)
; "\"1970-01-01\""

Tagged JSON

Adding support for lossless encoding data using tagged values. This includes both reading and writing support.

(def mapper
  (j/object-mapper
    {:encode-key-fn true
     :decode-key-fn true
     :modules [(jt/module
                 {:handlers {Keyword {:tag "!kw"
                                      :encode jt/encode-keyword
                                      :decode keyword}
                             PersistentHashSet {:tag "!set"
                                                :encode jt/encode-collection
                                                :decode set}}})]}))

(-> {:system/status #{:status/good}}
    (j/write-value-as-string mapper)
    (doto prn)
    (j/read-value mapper))
; prints "{\"system/status\":[\"!set\",[[\"!kw\",\"status/good\"]]]}"
; => {:system/status #{:status/good}}

In simple perf tests, tagged JSON is much faster than EDN or Transit.

Streaming

See docs/streaming.md.

Performance

  • All standard encoders and decoders are written in Java
  • Protocol dispatch with read-value & write-value
  • Jackson ObjectMapper is used directly
  • Small functions to support JVM Inlining

Measured using lein-jmh, see perf-tests for details.

Throughput, relative

encode

decode

Throughput, absolute

encode

decode

Throughput, data

➜  jsonista git:(master) ✗ lein jmh '{:file "benchmarks.edn", :type :quick, :format :table}'
{:% 100.0 :eta "00:00:00"}

:benchmark                     :name    :mode        :samples  :score              :score-error  :params
-----------------------------  -------  -----------  --------  ------------------  ------------  --------------
jsonista.jmh/encode-data-json  :encode  :throughput  5         2011809.137  ops/s  12600.809     {:size "10b"}
jsonista.jmh/encode-data-json  :encode  :throughput  5         382677.707   ops/s  2861.142      {:size "100b"}
jsonista.jmh/encode-data-json  :encode  :throughput  5         66403.631    ops/s  597.436       {:size "1k"}
jsonista.jmh/encode-data-json  :encode  :throughput  5         5480.185     ops/s  58.379        {:size "10k"}
jsonista.jmh/encode-data-json  :encode  :throughput  5         576.691      ops/s  15.682        {:size "100k"}
jsonista.jmh/encode-cheshire   :encode  :throughput  5         996875.314   ops/s  5688.227      {:size "10b"}
jsonista.jmh/encode-cheshire   :encode  :throughput  5         482130.613   ops/s  2685.181      {:size "100b"}
jsonista.jmh/encode-cheshire   :encode  :throughput  5         128936.005   ops/s  879.709       {:size "1k"}
jsonista.jmh/encode-cheshire   :encode  :throughput  5         12209.066    ops/s  94.285        {:size "10k"}
jsonista.jmh/encode-cheshire   :encode  :throughput  5         1258.157     ops/s  12.340        {:size "100k"}
jsonista.jmh/encode-jsonista   :encode  :throughput  5         6356105.348  ops/s  85360.100     {:size "10b"}
jsonista.jmh/encode-jsonista   :encode  :throughput  5         2010379.039  ops/s  67648.165     {:size "100b"}
jsonista.jmh/encode-jsonista   :encode  :throughput  5         409264.663   ops/s  3704.992      {:size "1k"}
jsonista.jmh/encode-jsonista   :encode  :throughput  5         34527.245    ops/s  251.065       {:size "10k"}
jsonista.jmh/encode-jsonista   :encode  :throughput  5         2934.595     ops/s  15.858        {:size "100k"}
jsonista.jmh/encode-jackson    :encode  :throughput  5         6275467.563  ops/s  123578.482    {:size "10b"}
jsonista.jmh/encode-jackson    :encode  :throughput  5         2092035.098  ops/s  11417.613     {:size "100b"}
jsonista.jmh/encode-jackson    :encode  :throughput  5         408380.251   ops/s  10912.350     {:size "1k"}
jsonista.jmh/encode-jackson    :encode  :throughput  5         31992.554    ops/s  230.781       {:size "10k"}
jsonista.jmh/encode-jackson    :encode  :throughput  5         2887.485     ops/s  12.491        {:size "100k"}
jsonista.jmh/decode-data-json  :decode  :throughput  5         2257552.949  ops/s  23890.443     {:size "10b"}
jsonista.jmh/decode-data-json  :decode  :throughput  5         498261.935   ops/s  2348.572      {:size "100b"}
jsonista.jmh/decode-data-json  :decode  :throughput  5         85191.855    ops/s  321.961       {:size "1k"}
jsonista.jmh/decode-data-json  :decode  :throughput  5         7763.264     ops/s  250.502       {:size "10k"}
jsonista.jmh/decode-data-json  :decode  :throughput  5         771.691      ops/s  6.559         {:size "100k"}
jsonista.jmh/decode-cheshire   :decode  :throughput  5         1099821.870  ops/s  14796.659     {:size "10b"}
jsonista.jmh/decode-cheshire   :decode  :throughput  5         544013.773   ops/s  4122.539      {:size "100b"}
jsonista.jmh/decode-cheshire   :decode  :throughput  5         109517.975   ops/s  911.623       {:size "1k"}
jsonista.jmh/decode-cheshire   :decode  :throughput  5         10017.553    ops/s  50.871        {:size "10k"}
jsonista.jmh/decode-cheshire   :decode  :throughput  5         1014.003     ops/s  18.609        {:size "100k"}
jsonista.jmh/decode-jsonista   :decode  :throughput  5         3476196.425  ops/s  21535.641     {:size "10b"}
jsonista.jmh/decode-jsonista   :decode  :throughput  5         792773.466   ops/s  8209.591      {:size "100b"}
jsonista.jmh/decode-jsonista   :decode  :throughput  5         160180.797   ops/s  554.940       {:size "1k"}
jsonista.jmh/decode-jsonista   :decode  :throughput  5         14151.302    ops/s  107.906       {:size "10k"}
jsonista.jmh/decode-jsonista   :decode  :throughput  5         1508.829     ops/s  5.855         {:size "100k"}
jsonista.jmh/decode-jackson    :decode  :throughput  5         5145394.434  ops/s  84237.662     {:size "10b"}
jsonista.jmh/decode-jackson    :decode  :throughput  5         1339393.911  ops/s  6660.176      {:size "100b"}
jsonista.jmh/decode-jackson    :decode  :throughput  5         274465.912   ops/s  1589.614      {:size "1k"}
jsonista.jmh/decode-jackson    :decode  :throughput  5         29607.044    ops/s  183.068       {:size "10k"}
jsonista.jmh/decode-jackson    :decode  :throughput  5         2539.491     ops/s  17.753        {:size "100k"}

Making a release

  • Update CHANGELOG.md and increment the version number in project.clj
  • Commit and push to Github
  • Create a Github release here
    • Use the version number of the release for the tag name
  • The Github Actions release workflow should fire and deploy a release to clojars

License

Copyright © 2016-2021 Metosin Oy.

Distributed under the Eclipse Public License 2.0.