diff --git a/build.gradle b/build.gradle index 6f68ba0..b4471e7 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,7 @@ java { } repositories { + mavenLocal() mavenCentral() } @@ -74,7 +75,7 @@ dependencies { implementation 'jakarta.json:jakarta.json-api:2.1.3' implementation 'org.glassfish:jakarta.json:2.0.1' // json-io - implementation 'com.cedarsoftware:json-io:4.88.0' + implementation 'com.cedarsoftware:json-io:4.102.0' // json-simple implementation 'com.googlecode.json-simple:json-simple:1.1.1' // json-smart diff --git a/src/main/java/com/github/fabienrenaud/jjb/databind/Deserialization.java b/src/main/java/com/github/fabienrenaud/jjb/databind/Deserialization.java index c31bdc3..a9e7689 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/databind/Deserialization.java +++ b/src/main/java/com/github/fabienrenaud/jjb/databind/Deserialization.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson2.JSON; import com.bluelinelabs.logansquare.LoganSquare; +import com.cedarsoftware.io.JsonIo; import com.github.fabienrenaud.jjb.JsonBench; import com.github.fabienrenaud.jjb.data.JsonSource; import com.google.gson.JsonSyntaxException; @@ -161,4 +162,11 @@ public Object wast() throws Exception { public Object djomo() throws Exception { return JSON_SOURCE().provider().djomo().fromString(JSON_SOURCE().nextString(), JSON_SOURCE().pojoType()); } + + @Benchmark + @Override + public Object jsonio() { + // Default ReadOptions; json-io infers field types from the target Class. + return JsonIo.toJava(JSON_SOURCE().nextString(), null).asClass(JSON_SOURCE().pojoType()); + } } diff --git a/src/main/java/com/github/fabienrenaud/jjb/databind/Serialization.java b/src/main/java/com/github/fabienrenaud/jjb/databind/Serialization.java index 46303c7..ce9d477 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/databind/Serialization.java +++ b/src/main/java/com/github/fabienrenaud/jjb/databind/Serialization.java @@ -2,6 +2,7 @@ import com.alibaba.fastjson2.JSON; import com.bluelinelabs.logansquare.LoganSquare; +import com.cedarsoftware.io.JsonIo; import com.github.fabienrenaud.jjb.JsonBench; import com.github.fabienrenaud.jjb.JsonUtils; import com.github.fabienrenaud.jjb.data.JsonSource; @@ -198,4 +199,13 @@ public Object djomo() throws Exception { JSON_SOURCE().provider().djomo().write(JSON_SOURCE().nextPojo(), baos); return baos; } + + @Benchmark + @Override + public Object jsonio() { + // standardJson() produces Jackson-compatible JSON: suppresses @type, @id/@ref, + // root type info; stringifies non-String map keys; emits ISO-8601 dates. + // Cached on the provider to match how Jackson/Gson/etc. cache their mappers. + return JsonIo.toJson(JSON_SOURCE().nextPojo(), JSON_SOURCE().provider().jsonioWriteOptions()); + } } diff --git a/src/main/java/com/github/fabienrenaud/jjb/provider/ClientsJsonProvider.java b/src/main/java/com/github/fabienrenaud/jjb/provider/ClientsJsonProvider.java index 6292e78..b975419 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/provider/ClientsJsonProvider.java +++ b/src/main/java/com/github/fabienrenaud/jjb/provider/ClientsJsonProvider.java @@ -1,5 +1,7 @@ package com.github.fabienrenaud.jjb.provider; +import com.cedarsoftware.io.WriteOptions; +import com.cedarsoftware.io.WriteOptionsBuilder; import com.dslplatform.json.DslJson; import com.dslplatform.json.runtime.Settings; import com.fasterxml.jackson.core.JsonFactory; @@ -162,6 +164,11 @@ public void toJson(com.squareup.moshi.JsonWriter writer, @Nullable OffsetDateTim private final com.bigcloud.djomo.Json djomo = new com.bigcloud.djomo.Json(); + // json-io WriteOptions is immutable+thread-safe once built; cache one instance per provider + // to match how every other library here caches its mapper/parser/serializer. Read-side uses + // JsonIo.toMaps()/toJava() which manage their own option caches internally. + private final WriteOptions jsonioWriteOptions = new WriteOptionsBuilder().standardJson().build(); + public ClientsJsonProvider() { // set johnzon JsonReader (default is `JsonProvider.provider()`) @@ -291,6 +298,11 @@ public com.bigcloud.djomo.Json djomo() { return djomo; } + @Override + public WriteOptions jsonioWriteOptions() { + return jsonioWriteOptions; + } + private static final ThreadLocal QUICKBUF_MESSAGE = ThreadLocal.withInitial(QuickbufSchema.Clients::newInstance); private static final ThreadLocal QUICKBUF_SINK = ThreadLocal.withInitial(() -> JsonSink.newInstance() .setPrettyPrinting(false) diff --git a/src/main/java/com/github/fabienrenaud/jjb/provider/JsonProvider.java b/src/main/java/com/github/fabienrenaud/jjb/provider/JsonProvider.java index df9d4f2..3807503 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/provider/JsonProvider.java +++ b/src/main/java/com/github/fabienrenaud/jjb/provider/JsonProvider.java @@ -1,5 +1,6 @@ package com.github.fabienrenaud.jjb.provider; +import com.cedarsoftware.io.WriteOptions; import com.dslplatform.json.DslJson; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,4 +63,6 @@ public interface JsonProvider { com.bigcloud.djomo.Json djomo(); + WriteOptions jsonioWriteOptions(); + } diff --git a/src/main/java/com/github/fabienrenaud/jjb/provider/UsersJsonProvider.java b/src/main/java/com/github/fabienrenaud/jjb/provider/UsersJsonProvider.java index 0c4eca5..9d96d4d 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/provider/UsersJsonProvider.java +++ b/src/main/java/com/github/fabienrenaud/jjb/provider/UsersJsonProvider.java @@ -1,5 +1,7 @@ package com.github.fabienrenaud.jjb.provider; +import com.cedarsoftware.io.WriteOptions; +import com.cedarsoftware.io.WriteOptionsBuilder; import com.dslplatform.json.DslJson; import com.dslplatform.json.runtime.Settings; import com.fasterxml.jackson.core.JsonFactory; @@ -58,6 +60,11 @@ public class UsersJsonProvider implements JsonProvider { private final com.bigcloud.djomo.Json djomo = new com.bigcloud.djomo.Json(); + // json-io WriteOptions is immutable+thread-safe once built; cache one instance per provider + // to match how every other library here caches its mapper/parser/serializer. Read-side uses + // JsonIo.toMaps()/toJava() which manage their own option caches internally. + private final WriteOptions jsonioWriteOptions = new WriteOptionsBuilder().standardJson().build(); + public UsersJsonProvider() { // set johnzon JsonReader (default is `JsonProvider.provider()`) @@ -187,6 +194,11 @@ public com.bigcloud.djomo.Json djomo() { return djomo; } + @Override + public WriteOptions jsonioWriteOptions() { + return jsonioWriteOptions; + } + private static final ThreadLocal QUICKBUF_MESSAGE = ThreadLocal.withInitial(QuickbufSchema.Users::newInstance); private static final ThreadLocal QUICKBUF_SINK = ThreadLocal.withInitial(() -> JsonSink.newInstance() .setPrettyPrinting(false) diff --git a/src/main/java/com/github/fabienrenaud/jjb/stream/Deserialization.java b/src/main/java/com/github/fabienrenaud/jjb/stream/Deserialization.java index d80dc69..3ba33e4 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/stream/Deserialization.java +++ b/src/main/java/com/github/fabienrenaud/jjb/stream/Deserialization.java @@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicReference; import com.cedarsoftware.io.JsonIo; -import com.cedarsoftware.io.ReadOptionsBuilder; +import com.cedarsoftware.io.JsonObject; import org.json.JSONException; import org.json.JSONObject; import org.openjdk.jmh.annotations.Benchmark; @@ -93,13 +93,14 @@ public Object genson() throws Exception { @Benchmark @Override public Object jsonio() { - - // returnAsNativeJsonObjects maps to old JsonWriter.USE_MAPS=true behavior, - // see {@link JsonIo#getReadOptionsBuilder(java.util.Map)} and - // the v4.19 changelog - return JsonIo.toObjects( - JSON_SOURCE().nextInputStream(), new ReadOptionsBuilder().returnAsNativeJsonObjects().build(), null - ); + // toMaps() returns the JsonObject (Map) graph directly via MapResolver, + // skipping the second-pass POJO field injection that toJava() does. + // ~45% faster on JsonPerformanceTest. The API handles Maps-mode option + // configuration internally via its own cache, so no options needed here. + // Explicit JsonObject target keeps the result as the native lite Map + // representation (the round-trip test in JsonBenchmark.test detects + // JsonObject and re-serializes it for verification). + return JsonIo.toMaps(JSON_SOURCE().nextInputStream()).asClass(JsonObject.class); } @Benchmark diff --git a/src/main/java/com/github/fabienrenaud/jjb/stream/Serialization.java b/src/main/java/com/github/fabienrenaud/jjb/stream/Serialization.java index c67cfbf..3a3c8eb 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/stream/Serialization.java +++ b/src/main/java/com/github/fabienrenaud/jjb/stream/Serialization.java @@ -5,7 +5,6 @@ import java.io.Writer; import com.cedarsoftware.io.JsonIo; -import com.cedarsoftware.io.WriteOptionsBuilder; import org.openjdk.jmh.annotations.Benchmark; import com.fasterxml.jackson.core.JsonGenerator; @@ -96,9 +95,9 @@ public Object genson() throws Exception { @Benchmark @Override public Object jsonio() { - - // showTypeInfoNever maps to old TYPE=false behavior see {@link JsonIo#getWriteOptionsBuilder(java.util.Map)} - return JsonIo.toJson(JSON_SOURCE().nextPojo(), new WriteOptionsBuilder().showTypeInfoNever().build()); + // standardJson() produces Jackson-compatible JSON: suppresses @type, @id/@ref, + // root type info; stringifies non-String map keys; emits ISO-8601 dates. + return JsonIo.toJson(JSON_SOURCE().nextPojo(), JSON_SOURCE().provider().jsonioWriteOptions()); } @Benchmark diff --git a/src/main/java/com/github/fabienrenaud/jjb/support/BenchSupport.java b/src/main/java/com/github/fabienrenaud/jjb/support/BenchSupport.java index 85922c7..e34ddf7 100644 --- a/src/main/java/com/github/fabienrenaud/jjb/support/BenchSupport.java +++ b/src/main/java/com/github/fabienrenaud/jjb/support/BenchSupport.java @@ -17,7 +17,7 @@ public enum BenchSupport { new Libapi(Library.JAKARTAJSON, Api.STREAM), new Libapi(Library.FLEXJSON, Api.DATABIND), new Libapi(Library.FASTJSON, Api.DATABIND), - new Libapi(Library.JSONIO, Api.STREAM), + new Libapi(Library.JSONIO, Api.DATABIND, Api.STREAM), new Libapi(Library.BOON, Api.DATABIND), new Libapi(Library.JOHNZON, Api.DATABIND), new Libapi(Library.JSONSMART, Api.DATABIND), @@ -52,7 +52,7 @@ public enum BenchSupport { new Libapi(Library.JAKARTAJSON), new Libapi(Library.FLEXJSON, Api.DATABIND), new Libapi(Library.FASTJSON, Api.DATABIND), - new Libapi(Library.JSONIO), + new Libapi(Library.JSONIO, Api.DATABIND), new Libapi(false, Library.BOON, Api.DATABIND), new Libapi(false, Library.JOHNZON, Api.DATABIND), new Libapi(false, Library.JSONSMART, Api.DATABIND), diff --git a/src/test/java/com/github/fabienrenaud/jjb/JsonBenchmark.java b/src/test/java/com/github/fabienrenaud/jjb/JsonBenchmark.java index f76126d..8f45e1d 100644 --- a/src/test/java/com/github/fabienrenaud/jjb/JsonBenchmark.java +++ b/src/test/java/com/github/fabienrenaud/jjb/JsonBenchmark.java @@ -36,7 +36,7 @@ protected void test(Library lib, Object o) { if (o instanceof Users || o instanceof Clients) { testPojo((T) o); } else if (o instanceof com.cedarsoftware.io.JsonObject) { - String v = com.cedarsoftware.io.JsonIo.toJson(o, new WriteOptionsBuilder().showTypeInfoNever().build()); + String v = com.cedarsoftware.io.JsonIo.toJson(o, new WriteOptionsBuilder().standardJson().build()); testString(v); } else if (o instanceof com.grack.nanojson.JsonObject) { String v = com.grack.nanojson.JsonWriter.string(o);