metosin / jsonista

Clojure library for fast JSON encoding and decoding.
https://cljdoc.org/d/metosin/jsonista
Eclipse Public License 2.0
422 stars 30 forks source link

Add tagged value module #36

Closed jeaye closed 3 years ago

jeaye commented 4 years ago

This aims to open the doors for jsonista to become a viable alternative for people currently using EDN with transit by providing a way to encode EDN types using a tagged JSON list. Currently, only keywords are supported.

Regarding TaggedValueOrPersistentVectorDeserializer, it's worth noting that it doesn't just peek for a tag and then fall back on the default PersistentVectorDeserializer, since peeking seemed to require a call to nextValue() and I found no way to backtack. So it seems I need to try building the transient and then just special-case the zeroth element to check for the tag.

Right now, it doesn't do anything special with mapping tags to different types in the Java code, since there's only one and I figure that change can be made when it's needed. The Clojure API, however, remains more general, since the tag needs to come in as a key. This will make adding new keys to the API trivial, if more types are supported in the future.

This closes #35.

jeaye commented 4 years ago

Ok, I've reworked this a bit.

  1. Removed stringer.core dep
  2. Added README documentation
  3. Extended tests to include keywords in vectors
  4. Reimplemented the serialization to not use writeRawValue, so now it will work with pretty printing

This is about all the time I have for this, so, if you're interested, please do merge. Otherwise, we'll just run with the fork.

Thanks for the help with this!

Benchmarks

These were timed using some chunk of production data and criterium's quick-bench.

Read

Write

ikitommi commented 3 years ago

Did a quick spike on making this more generic. What do you think of this:

(def mapper
  (j/object-mapper
    {: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}}})]}))

(-> {:kikka #{:kukka :kakka}}
    (j/write-value-as-string mapper)
    (doto prn)
    (j/read-value mapper))
; prints "{\"kikka\":[\"!set\",[[\"!kw\",\"kukka\"],[\"!kw\",\"kakka\"]]]}"
; => {:kikka #{:kukka :kakka}}
jeaye commented 3 years ago

Yeah, I dig it. We ended up also adding set support, but I don't think I force pushed it yet, since there didn't seem to be any interest in this PR. Your proposal is much more general, so I think it's a good way forward. In terms of the tagged values working, we've seen no issues running this in production since October.

ikitommi commented 3 years ago

Thanks for the PR and the perf report! will modify a bit to be generic.

ikitommi commented 3 years ago

https://github.com/metosin/jsonista/pull/39

ikitommi commented 3 years ago

given data:

{:results [{:tags [:jsonista.json-perf-test/kikka :jsonista.json-perf-test/kukka],
            :email "morris.lambert@example.com",
            :phone "08-2274-7839",
            :name {:title "mr", :first "morris", :last "lambert"},
            :nat "AU",
            :created #inst"2020-12-26T11:01:29.531-00:00",
            :login {:username "smallbird414",
                    :password "carole",
                    :salt "yO9OBSsk",
                    :md5 "658323a603522238fb32a86b82eafd55",
                    :sha1 "289f6e9a8ccd42b539e0c43283e788aeb8cd0f6e",
                    :sha256 "57bca99b2b4e78aa2171eda4db3f35e7631ca3b30f157bdc7ea089a855c66668"},
            :dob "1950-07-13 09:18:34",
            :id {:name "TFN", :value "740213762"},
            :picture {:large "https://randomuser.me/api/portraits/men/95.jpg",
                      :medium "https://randomuser.me/api/portraits/med/men/95.jpg",
                      :thumbnail "https://randomuser.me/api/portraits/thumb/men/95.jpg"},
            :gender "male",
            :registered "2012-04-07 00:05:32",
            :cell "0452-558-702",
            :location {:street "7239 hillcrest rd",
                       :city "nowra",
                       :state "australian capital territory",
                       :postcode 7541}}],
 :info {:seed "fb0c2b3c7cedc7af", :results 1, :page 1, :version "1.1"}}

some numbers:

    ;; 28µs
    (title "jsonista-tagged")
    (cc/quick-bench
      (j/read-value (j/write-value-as-bytes data mapper) mapper))

    ;; 190µs
    (title "edn")
    (cc/quick-bench
      (edn/read-string (pr-str data)))

    ;; 82µs
    (title "transit")
    (cc/quick-bench
      (<-transit (->transit data)))

    ;; 53µs
    (title "nippy")
    (cc/quick-bench
      (nippy/thaw (nippy/freeze data)))