diff --git a/CHANGELOG.md b/CHANGELOG.md index e49e943..37741e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## UNRELEASED + +* implement `com.fasterxml.jackson.databind.deser.ContextualDeserializer` for 30% faster de-serialization of Maps and Vectors. +* Updated deps: + +```clj +[com.fasterxml.jackson.core/jackson-core "2.13.0"] is available but we use "2.12.5" +[com.fasterxml.jackson.core/jackson-databind "2.13.0"] is available but we use "2.12.5" +[com.fasterxml.jackson.datatype/jackson-datatype-jsr310 "2.13.0"] is available but we use "2.12.5" +``` + ## 0.3.4 (2021-09-16) * add `deps.edn` to the project diff --git a/README.md b/README.md index 9d79339..bf55d4b 100644 --- a/README.md +++ b/README.md @@ -159,48 +159,48 @@ see [perf-tests](/test/jsonista/jmh.clj) for details. ➜ 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 1905463.089 ops/s 63122.305 {:size "10b"} -jsonista.jmh/encode-data-json :encode :throughput 5 345349.400 ops/s 175312.845 {:size "100b"} -jsonista.jmh/encode-data-json :encode :throughput 5 55137.457 ops/s 20339.204 {:size "1k"} -jsonista.jmh/encode-data-json :encode :throughput 5 5194.837 ops/s 395.984 {:size "10k"} -jsonista.jmh/encode-data-json :encode :throughput 5 539.111 ops/s 219.494 {:size "100k"} -jsonista.jmh/encode-cheshire :encode :throughput 5 965841.282 ops/s 384153.551 {:size "10b"} -jsonista.jmh/encode-cheshire :encode :throughput 5 414779.218 ops/s 77713.579 {:size "100b"} -jsonista.jmh/encode-cheshire :encode :throughput 5 95781.504 ops/s 16569.179 {:size "1k"} -jsonista.jmh/encode-cheshire :encode :throughput 5 9114.238 ops/s 824.365 {:size "10k"} -jsonista.jmh/encode-cheshire :encode :throughput 5 888.121 ops/s 190.972 {:size "100k"} -jsonista.jmh/encode-jsonista :encode :throughput 5 4354049.558 ops/s 1215677.321 {:size "10b"} -jsonista.jmh/encode-jsonista :encode :throughput 5 1698787.064 ops/s 940540.979 {:size "100b"} -jsonista.jmh/encode-jsonista :encode :throughput 5 376730.022 ops/s 154961.364 {:size "1k"} -jsonista.jmh/encode-jsonista :encode :throughput 5 29272.337 ops/s 5208.374 {:size "10k"} -jsonista.jmh/encode-jsonista :encode :throughput 5 2275.436 ops/s 1329.558 {:size "100k"} -jsonista.jmh/encode-jackson :encode :throughput 5 5471224.116 ops/s 2598678.138 {:size "10b"} -jsonista.jmh/encode-jackson :encode :throughput 5 1509416.980 ops/s 497578.108 {:size "100b"} -jsonista.jmh/encode-jackson :encode :throughput 5 386633.021 ops/s 1158.922 {:size "1k"} -jsonista.jmh/encode-jackson :encode :throughput 5 31780.542 ops/s 2320.829 {:size "10k"} -jsonista.jmh/encode-jackson :encode :throughput 5 2735.888 ops/s 406.603 {:size "100k"} -jsonista.jmh/decode-data-json :decode :throughput 5 2094889.246 ops/s 89077.322 {:size "10b"} -jsonista.jmh/decode-data-json :decode :throughput 5 485354.569 ops/s 23670.883 {:size "100b"} -jsonista.jmh/decode-data-json :decode :throughput 5 83811.653 ops/s 3133.162 {:size "1k"} -jsonista.jmh/decode-data-json :decode :throughput 5 7741.879 ops/s 155.608 {:size "10k"} -jsonista.jmh/decode-data-json :decode :throughput 5 797.233 ops/s 14.762 {:size "100k"} -jsonista.jmh/decode-cheshire :decode :throughput 5 1433093.847 ops/s 90976.064 {:size "10b"} -jsonista.jmh/decode-cheshire :decode :throughput 5 450070.726 ops/s 131950.612 {:size "100b"} -jsonista.jmh/decode-cheshire :decode :throughput 5 91872.057 ops/s 6432.237 {:size "1k"} -jsonista.jmh/decode-cheshire :decode :throughput 5 8509.100 ops/s 156.138 {:size "10k"} -jsonista.jmh/decode-cheshire :decode :throughput 5 887.882 ops/s 33.875 {:size "100k"} -jsonista.jmh/decode-jsonista :decode :throughput 5 2608856.640 ops/s 474804.764 {:size "10b"} -jsonista.jmh/decode-jsonista :decode :throughput 5 607787.943 ops/s 13728.880 {:size "100b"} -jsonista.jmh/decode-jsonista :decode :throughput 5 120542.299 ops/s 39147.980 {:size "1k"} -jsonista.jmh/decode-jsonista :decode :throughput 5 10827.731 ops/s 5616.145 {:size "10k"} -jsonista.jmh/decode-jsonista :decode :throughput 5 1128.572 ops/s 232.442 {:size "100k"} -jsonista.jmh/decode-jackson :decode :throughput 5 5526592.411 ops/s 1463758.000 {:size "10b"} -jsonista.jmh/decode-jackson :decode :throughput 5 1316257.448 ops/s 163658.831 {:size "100b"} -jsonista.jmh/decode-jackson :decode :throughput 5 252440.943 ops/s 14566.399 {:size "1k"} -jsonista.jmh/decode-jackson :decode :throughput 5 23871.633 ops/s 1625.629 {:size "10k"} -jsonista.jmh/decode-jackson :decode :throughput 5 2211.046 ops/s 91.282 {:size "100k"} +: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 diff --git a/benchmarks.edn b/benchmarks.edn index adb8f90..f65d6b4 100644 --- a/benchmarks.edn +++ b/benchmarks.edn @@ -1,10 +1,10 @@ {:benchmarks [{:name :encode :ns jsonista.jmh - :fn [encode-data-json encode-cheshire encode-jsonista encode-jackson] + :fn [encode-data-json encode-cheshire encode-jsonista encode-jackson #_encode-jsonista-fast] :args [:state/edn]} {:name :decode :ns jsonista.jmh - :fn [decode-data-json decode-cheshire decode-jsonista decode-jackson] + :fn [decode-data-json decode-cheshire decode-jsonista decode-jackson #_decode-jsonista-fast] :args [:state/json]}] :states {:json {:fn jsonista.jmh/json-data, :args [:param/size]} :edn {:fn jsonista.jmh/edn-data, :args [:param/size]}} diff --git a/docs/json-decode-t.png b/docs/json-decode-t.png index f20ece0..964e297 100644 Binary files a/docs/json-decode-t.png and b/docs/json-decode-t.png differ diff --git a/docs/json-decode.png b/docs/json-decode.png index 77aba07..e5cd12e 100644 Binary files a/docs/json-decode.png and b/docs/json-decode.png differ diff --git a/docs/json-encode-t.png b/docs/json-encode-t.png index 7505b13..0ad8ab4 100644 Binary files a/docs/json-encode-t.png and b/docs/json-encode-t.png differ diff --git a/docs/json-encode.png b/docs/json-encode.png index 6d87f96..2454a88 100644 Binary files a/docs/json-encode.png and b/docs/json-encode.png differ diff --git a/project.clj b/project.clj index 2ef6553..a91df07 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,7 @@ :resource-paths ["resources"] :javac-options ["-Xlint:unchecked" "-target" "1.8" "-source" "1.8"] :java-source-paths ["src/java"] - :plugins [[lein-codox "0.10.7"] + :plugins [[lein-codox "0.10.8"] [lein-jmh "0.3.0"]] :deploy-repositories [["releases" {:url "https://repo.clojars.org/" :sign-releases false @@ -16,13 +16,13 @@ :codox {:source-uri "http://github.com/metosin/jsonista/blob/master/{filepath}#L{line}" :output-path "doc" :metadata {:doc/format :markdown}} - :dependencies [[com.fasterxml.jackson.core/jackson-core "2.12.5"] - [com.fasterxml.jackson.core/jackson-databind "2.12.5"] - [com.fasterxml.jackson.datatype/jackson-datatype-jsr310 "2.12.5"]] + :dependencies [[com.fasterxml.jackson.core/jackson-core "2.13.0"] + [com.fasterxml.jackson.core/jackson-databind "2.13.0"] + [com.fasterxml.jackson.datatype/jackson-datatype-jsr310 "2.13.0"]] :profiles {:provided {:dependencies [[org.clojure/clojure "1.10.1"]]} :dev {:dependencies [[org.clojure/clojure "1.10.1"] [jmh-clojure/jmh-clojure "0.4.1"] - [com.fasterxml.jackson.datatype/jackson-datatype-joda "2.12.5"] + [com.fasterxml.jackson.datatype/jackson-datatype-joda "2.13.0"] [cheshire "5.10.1"] [com.taoensso/nippy "3.1.1"] [org.clojure/data.json "2.4.0"] @@ -30,6 +30,7 @@ [org.msgpack/msgpack-core "0.9.0"] [org.msgpack/jackson-dataformat-msgpack "0.9.0" :exclusions [com.fasterxml.jackson.core/jackson-databind]] + [com.clojure-goes-fast/clj-async-profiler "0.5.1"] [criterium "0.4.6"]] :global-vars {*warn-on-reflection* true}} :virgil {:plugins [[lein-virgil "0.1.9"]]} diff --git a/src/clj/jsonista/core.clj b/src/clj/jsonista/core.clj index 649c998..363c6ae 100644 --- a/src/clj/jsonista/core.clj +++ b/src/clj/jsonista/core.clj @@ -54,6 +54,8 @@ FunctionalSerializer KeywordSerializer KeywordKeyDeserializer + HashMapDeserializer + ArrayListDeserializer PersistentHashMapDeserializer PersistentVectorDeserializer SymbolSerializer @@ -98,6 +100,13 @@ (true? encode-key-fn) (.addKeySerializer Keyword (KeywordSerializer. true)) (fn? encode-key-fn) (.addKeySerializer Keyword (FunctionalKeywordSerializer. encode-key-fn))))) +(defn ^:no-doc ^Module java-collection-module + "Create a Jackson Databind module to support Java HashMap and ArrayList." + [] + (doto (SimpleModule. "JavaCollectionModule") + (.addDeserializer List (ArrayListDeserializer.)) + (.addDeserializer Map (HashMapDeserializer.)))) + (defn ^ObjectMapper object-mapper "Create an ObjectMapper with Clojure support. diff --git a/src/java/jsonista/jackson/ArrayListDeserializer.java b/src/java/jsonista/jackson/ArrayListDeserializer.java new file mode 100644 index 0000000..f92e684 --- /dev/null +++ b/src/java/jsonista/jackson/ArrayListDeserializer.java @@ -0,0 +1,48 @@ +package jsonista.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ArrayListDeserializer extends StdDeserializer> implements ContextualDeserializer { + + private JsonDeserializer _valueDeserializer; + + public ArrayListDeserializer() { + super(List.class); + } + + public ArrayListDeserializer(JsonDeserializer valueDeser) { + this(); + _valueDeserializer = valueDeser; + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty beanProperty) throws JsonMappingException { + JavaType object = ctxt.constructType(Object.class); + JsonDeserializer valueDeser = ctxt.findNonContextualValueDeserializer(object); + return this.withResolved(valueDeser); + } + + private JsonDeserializer> withResolved(JsonDeserializer valueDeser) { + return this._valueDeserializer == valueDeser ? this : new ArrayListDeserializer(valueDeser); + } + + @Override + @SuppressWarnings("unchecked") + public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + ArrayList list = new ArrayList<>(); + + JsonDeserializer deser = ctxt.findNonContextualValueDeserializer(ctxt.constructType(Object.class)); + while (p.nextValue() != JsonToken.END_ARRAY) { + list.add(deser.deserialize(p, ctxt)); + } + return list; + } +} diff --git a/src/java/jsonista/jackson/HashMapDeserializer.java b/src/java/jsonista/jackson/HashMapDeserializer.java new file mode 100644 index 0000000..ba045c8 --- /dev/null +++ b/src/java/jsonista/jackson/HashMapDeserializer.java @@ -0,0 +1,52 @@ +package jsonista.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HashMapDeserializer extends StdDeserializer> implements ContextualDeserializer { + + private KeyDeserializer _keyDeserializer; + private JsonDeserializer _valueDeserializer; + + public HashMapDeserializer() { + super(Map.class); + } + + public HashMapDeserializer(KeyDeserializer keyDeser, JsonDeserializer valueDeser) { + this(); + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + } + + protected HashMapDeserializer withResolved(KeyDeserializer keyDeser, JsonDeserializer valueDeser) { + return this._keyDeserializer == keyDeser && this._valueDeserializer == valueDeser ? this : new HashMapDeserializer(keyDeser, valueDeser); + } + + @Override + public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty beanProperty) throws JsonMappingException { + JavaType object = ctxt.constructType(Object.class); + KeyDeserializer keyDeser = ctxt.findKeyDeserializer(object, null); + JsonDeserializer valueDeser = ctxt.findNonContextualValueDeserializer(object); + return this.withResolved(keyDeser, valueDeser); + } + + @Override + @SuppressWarnings("unchecked") + public Map deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Map map = new HashMap<>(); + while (p.nextToken() != JsonToken.END_OBJECT) { + Object key = _keyDeserializer.deserializeKey(p.getCurrentName(), ctxt); + p.nextToken(); + Object value = _valueDeserializer.deserialize(p, ctxt); + map.put(key, value); + } + return map; + } +} diff --git a/src/java/jsonista/jackson/PersistentHashMapDeserializer.java b/src/java/jsonista/jackson/PersistentHashMapDeserializer.java index cf320fb..6383033 100644 --- a/src/java/jsonista/jackson/PersistentHashMapDeserializer.java +++ b/src/java/jsonista/jackson/PersistentHashMapDeserializer.java @@ -6,32 +6,48 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.util.Map; -public class PersistentHashMapDeserializer extends StdDeserializer> { +public class PersistentHashMapDeserializer extends StdDeserializer> implements ContextualDeserializer { + + private KeyDeserializer _keyDeserializer; + private JsonDeserializer _valueDeserializer; public PersistentHashMapDeserializer() { super(Map.class); } + public PersistentHashMapDeserializer(KeyDeserializer keyDeser, JsonDeserializer valueDeser) { + this(); + _keyDeserializer = keyDeser; + _valueDeserializer = valueDeser; + } + + protected PersistentHashMapDeserializer withResolved(KeyDeserializer keyDeser, JsonDeserializer valueDeser) { + return this._keyDeserializer == keyDeser && this._valueDeserializer == valueDeser ? this : new PersistentHashMapDeserializer(keyDeser, valueDeser); + } + @Override - @SuppressWarnings("unchecked") - public Map deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ITransientMap t = PersistentHashMap.EMPTY.asTransient(); + public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty beanProperty) throws JsonMappingException { JavaType object = ctxt.constructType(Object.class); KeyDeserializer keyDeser = ctxt.findKeyDeserializer(object, null); JsonDeserializer valueDeser = ctxt.findNonContextualValueDeserializer(object); + return this.withResolved(keyDeser, valueDeser); + } + + @Override + @SuppressWarnings("unchecked") + public Map deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + ITransientMap t = PersistentHashMap.EMPTY.asTransient(); while (p.nextToken() != JsonToken.END_OBJECT) { - Object key = keyDeser.deserializeKey(p.getCurrentName(), ctxt); + Object key = _keyDeserializer.deserializeKey(p.getCurrentName(), ctxt); p.nextToken(); - Object value = valueDeser.deserialize(p, ctxt); + Object value = _valueDeserializer.deserialize(p, ctxt); t = t.assoc(key, value); } diff --git a/src/java/jsonista/jackson/PersistentVectorDeserializer.java b/src/java/jsonista/jackson/PersistentVectorDeserializer.java index 954bb8c..2ef109d 100644 --- a/src/java/jsonista/jackson/PersistentVectorDeserializer.java +++ b/src/java/jsonista/jackson/PersistentVectorDeserializer.java @@ -5,28 +5,45 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.util.List; +import java.util.Map; -public class PersistentVectorDeserializer extends StdDeserializer> { +public class PersistentVectorDeserializer extends StdDeserializer> implements ContextualDeserializer { + + private JsonDeserializer _valueDeserializer; public PersistentVectorDeserializer() { super(List.class); } + protected PersistentVectorDeserializer(JsonDeserializer valueDeser) { + this(); + _valueDeserializer = valueDeser; + } + + @Override + public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty beanProperty) throws JsonMappingException { + JavaType object = ctxt.constructType(Object.class); + JsonDeserializer valueDeser = ctxt.findNonContextualValueDeserializer(object); + return this.withResolved(valueDeser); + } + + private JsonDeserializer> withResolved(JsonDeserializer valueDeser) { + return this._valueDeserializer == valueDeser ? this : new PersistentVectorDeserializer(valueDeser); + } + @Override @SuppressWarnings("unchecked") public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { ITransientCollection t = PersistentVector.EMPTY.asTransient(); - JsonDeserializer deser = ctxt.findNonContextualValueDeserializer(ctxt.constructType(Object.class)); while (p.nextValue() != JsonToken.END_ARRAY) { - t = t.conj(deser.deserialize(p, ctxt)); + t = t.conj(_valueDeserializer.deserialize(p, ctxt)); } - // t.persistent() returns a PersistentVector which is a list return (List) t.persistent(); } } diff --git a/test/jsonista/jmh.clj b/test/jsonista/jmh.clj index a95df8b..75eb000 100644 --- a/test/jsonista/jmh.clj +++ b/test/jsonista/jmh.clj @@ -35,6 +35,10 @@ (defn encode-jsonista [x] (j/write-value-as-string x)) (defn decode-jsonista [x] (j/read-value x)) +(let [mapper (j/object-mapper {:modules [(j/java-collection-module)]})] + (defn encode-jsonista-fast [x] (.writeValueAsString mapper x)) + (defn decode-jsonista-fast [x] (.readValue mapper ^String x ^Class Object))) + (let [mapper (ObjectMapper.)] (defn encode-jackson [x] (.writeValueAsString mapper x)) (defn decode-jackson [x] (.readValue mapper ^String x ^Class Object))) diff --git a/test/jsonista/json_perf_test.clj b/test/jsonista/json_perf_test.clj index 00c4cb7..d346281 100644 --- a/test/jsonista/json_perf_test.clj +++ b/test/jsonista/json_perf_test.clj @@ -60,12 +60,12 @@ (assert (= +data+ (cheshire/parse-string-strict +json+))) (cc/quick-bench (cheshire/parse-string-strict +json+)) - ;; 390ns + ;; 300ns (title "decode: jsonista") (assert (= +data+ (j/read-value +json+))) (cc/quick-bench (j/read-value +json+)) - ;; 230ns + ;; 260ns (title "decode: jackson") (let [mapper (ObjectMapper.) decode (fn [] (.readValue mapper +json+ Map))] @@ -118,20 +118,20 @@ (title file) - ; 1.0µs (10b) + ; 0.9µs (10b) ; 1.9µs (100b) ; 9.2µs (1k) - ; 102µs (10k) - ; 990µs (100k) + ; 110µs (10k) + ; 1000µs (100k) (title "decode: cheshire") (assert (= data (cheshire/parse-string json))) (cc/quick-bench (cheshire/parse-string json)) - ; 0.40µs (10b) - ; 1.5µs (100b) - ; 7.1µs (1k) - ; 77µs (10k) - ; 760µs (100k) + ; 0.32µs (10b) + ; 1.3µs (100b) + ; 5.9µs (1k) + ; 70µs (10k) + ; 680µs (100k) (title "decode: jsonista") (assert (= data (j/read-value json))) (cc/quick-bench (j/read-value json))))