From 313fdda8579dfd02e8d663250718b3822bdcf3c7 Mon Sep 17 00:00:00 2001 From: "robin.bygrave" Date: Thu, 18 Jun 2026 22:00:18 +1200 Subject: [PATCH] Refactor from Jackson Core to avaje-json-core --- docs/LIBRARY.md | 1 + docs/guides/AGENTS.md | 1 + docs/guides/README.md | 1 + ...ng-json-jackson-core-to-avaje-json-core.md | 91 +++++ ebean-api/pom.xml | 10 +- .../main/java/io/ebean/DatabaseBuilder.java | 16 +- .../java/io/ebean/config/ClassLoadConfig.java | 4 +- .../java/io/ebean/config/DatabaseConfig.java | 12 +- .../java/io/ebean/service/SpiJsonService.java | 28 +- .../main/java/io/ebean/text/json/EJson.java | 27 +- .../io/ebean/text/json/JsonBeanReader.java | 6 +- .../java/io/ebean/text/json/JsonContext.java | 57 ++- .../java/io/ebean/text/json/JsonWriter.java | 8 +- ebean-api/src/main/java/module-info.java | 2 +- ebean-core-json/pom.xml | 8 +- .../io/ebeaninternal/json/DJsonService.java | 99 +---- .../io/ebeaninternal/json/EJsonReader.java | 362 +++++------------- .../io/ebeaninternal/json/EJsonWriter.java | 281 ++++++-------- .../src/main/java/module-info.java | 3 +- ebean-core-type/pom.xml | 7 +- .../java/io/ebean/core/type/ScalarType.java | 43 ++- .../ebean/core/type/ScalarTypeBaseDate.java | 33 +- .../core/type/ScalarTypeBaseDateTime.java | 57 ++- .../core/type/ScalarTypeBaseVarchar.java | 15 +- .../src/main/java/module-info.java | 3 +- ebean-core/pom.xml | 8 - .../io/ebeaninternal/api/SpiJsonContext.java | 4 +- .../ebeaninternal/api/json/SpiJsonReader.java | 12 +- .../server/changelog/ChangeJsonBuilder.java | 59 +-- .../server/core/InternalConfiguration.java | 8 +- .../server/deploy/BeanChangeJson.java | 64 +--- .../server/deploy/BeanDescriptor.java | 15 +- .../BeanDescriptorElementEmbeddedMap.java | 22 +- .../deploy/BeanDescriptorElementScalar.java | 15 +- .../BeanDescriptorElementScalarMap.java | 22 +- .../server/deploy/BeanDescriptorJsonHelp.java | 87 ++--- .../server/deploy/BeanProperty.java | 9 +- .../server/deploy/BeanPropertyAssocMany.java | 117 ++++-- .../deploy/BeanPropertyAssocManyJsonHelp.java | 15 +- .../BeanPropertyAssocManyJsonTransient.java | 4 +- .../server/json/DJsonBeanReader.java | 4 +- .../server/json/DJsonContext.java | 216 ++++++----- .../server/json/DJsonScalar.java | 13 +- .../ebeaninternal/server/json/ReadJson.java | 40 +- .../ebeaninternal/server/json/WriteJson.java | 250 ++++-------- .../readaudit/DefaultReadAuditLogger.java | 71 ++-- .../server/type/ScalarTypeArrayList.java | 14 +- .../server/type/ScalarTypeArraySet.java | 14 +- .../server/type/ScalarTypeBigDecimal.java | 12 +- .../server/type/ScalarTypeBoolean.java | 39 +- .../server/type/ScalarTypeByte.java | 15 +- .../server/type/ScalarTypeBytesBase.java | 15 +- .../server/type/ScalarTypeBytesEncrypted.java | 15 +- .../server/type/ScalarTypeCharArray.java | 6 +- .../server/type/ScalarTypeDouble.java | 12 +- .../server/type/ScalarTypeDuration.java | 12 +- .../type/ScalarTypeEncryptedWrapper.java | 8 +- .../server/type/ScalarTypeEnumStandard.java | 21 +- .../server/type/ScalarTypeFile.java | 15 +- .../server/type/ScalarTypeFloat.java | 12 +- .../server/type/ScalarTypeInteger.java | 12 +- .../server/type/ScalarTypeJsonList.java | 10 +- .../server/type/ScalarTypeJsonMap.java | 10 +- .../server/type/ScalarTypeJsonSet.java | 10 +- .../server/type/ScalarTypeLocalDateTime.java | 19 +- .../server/type/ScalarTypeLocalTime.java | 12 +- .../server/type/ScalarTypeLong.java | 12 +- .../server/type/ScalarTypeMathBigInteger.java | 12 +- .../server/type/ScalarTypeMonthDay.java | 12 +- .../server/type/ScalarTypeNotFound.java | 8 +- .../server/type/ScalarTypePostgresHstore.java | 10 +- .../server/type/ScalarTypeShort.java | 12 +- .../server/type/ScalarTypeStringBase.java | 15 +- .../server/type/ScalarTypeTime.java | 12 +- .../server/type/ScalarTypeUUIDBase.java | 12 +- .../server/type/ScalarTypeWrapper.java | 8 +- .../server/type/ScalarTypeYear.java | 12 +- .../server/util/JsonContentHash.java | 152 ++++---- ebean-core/src/main/java/module-info.java | 2 +- .../ebeaninternal/server/type/JsonTester.java | 29 +- .../server/type/ScalarTypeArrayListTest.java | 15 +- .../server/type/ScalarTypeArraySetH2Test.java | 14 +- .../server/type/ScalarTypeBooleanTest.java | 37 +- .../type/ScalarTypeLocalDateTimeTest.java | 17 +- .../type/ScalarTypePostgresHstoreTest.java | 24 +- ebean-jackson-mapper/pom.xml | 7 + .../mapper/ScalarJsonJacksonMapper.java | 19 +- .../src/main/java/module-info.java | 1 + .../io/ebean/postgis/ScalarTypePgisBase.java | 8 +- .../io/ebean/pgvector/ScalarTypePGbase.java | 8 +- .../io/ebean/postgis/ScalarTypePgisBase.java | 8 +- .../postgis/latte/ScalarTypeGeoLatteBase.java | 8 +- .../src/main/java/io/ebean/test/DbJson.java | 8 +- .../server/text/json/DJsonScalarTest.java | 26 +- .../server/text/json/WriteJsonDirtyTest.java | 10 +- .../server/text/json/WriteJsonTest.java | 6 +- .../java/io/ebean/xtest/json/EJsonTests.java | 19 +- .../ebean/xtest/json/JsonBeanReaderTest.java | 14 +- .../io/ebean/xtest/json/JsonContextTest.java | 33 +- .../json/TestJsonBeanDescriptorParse.java | 7 +- .../resources/bean/example-list-contains.json | 2 - .../resources/bean/example-list-match.json | 29 +- pom.xml | 1 + 103 files changed, 1490 insertions(+), 1652 deletions(-) create mode 100644 docs/guides/migrating-json-jackson-core-to-avaje-json-core.md diff --git a/docs/LIBRARY.md b/docs/LIBRARY.md index 82e27c985..9e8b86fb5 100644 --- a/docs/LIBRARY.md +++ b/docs/LIBRARY.md @@ -183,6 +183,7 @@ database.save(customer); | Configure database and `Database` bean | [add-ebean-postgres-database-config.md](guides/add-ebean-postgres-database-config.md) | | Add PostgreSQL test container support | [add-ebean-postgres-test-container.md](guides/add-ebean-postgres-test-container.md) | | Generate DB migrations | [add-ebean-db-migration-generation.md](guides/add-ebean-db-migration-generation.md) | +| Migrate JSON APIs from Jackson core to avaje-json-core | [migrating-json-jackson-core-to-avaje-json-core.md](guides/migrating-json-jackson-core-to-avaje-json-core.md) | | Model entity beans correctly | [entity-bean-creation.md](guides/entity-bean-creation.md) | | Use Lombok safely with entities | [lombok-with-ebean-entity-beans.md](guides/lombok-with-ebean-entity-beans.md) | | Write type-safe query bean queries | [writing-ebean-query-beans.md](guides/writing-ebean-query-beans.md) | diff --git a/docs/guides/AGENTS.md b/docs/guides/AGENTS.md index 0fec50b33..4109ab263 100644 --- a/docs/guides/AGENTS.md +++ b/docs/guides/AGENTS.md @@ -12,6 +12,7 @@ Key guides (fetch and follow when performing the relevant task): - Maven POM setup: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-maven-pom.md - Database configuration: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-database-config.md - Migrate to `Database.builder()`: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/migrating-to-database-builder.md +- Migrate JSON APIs from Jackson core to avaje-json-core: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/migrating-json-jackson-core-to-avaje-json-core.md - Write queries with query beans: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/writing-ebean-query-beans.md - Persisting and transactions: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/persisting-and-transactions-with-ebean.md - Query metrics and naming: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-metrics.md diff --git a/docs/guides/README.md b/docs/guides/README.md index b85a87b7b..a9432d6c5 100644 --- a/docs/guides/README.md +++ b/docs/guides/README.md @@ -23,6 +23,7 @@ existing Maven project. Complete the steps in order. | Guide | Description | |-------|-------------| | [Migrate to `Database.builder()`](migrating-to-database-builder.md) | Replace legacy `new DatabaseConfig()` and `DatabaseFactory.create(...)` code with `Database.builder()` and `DatabaseBuilder.build()`. Includes common rewrites, fluent builder equivalents, and manual-review cases for semi-automated upgrades | +| [Migrate JSON APIs from Jackson core to avaje-json-core](migrating-json-jackson-core-to-avaje-json-core.md) | Cut over `JsonParser`/`JsonGenerator`/`JsonFactory` usage to `JsonReader`/`JsonWriter`/`JsonStream`, including `DatabaseBuilder`/`DatabaseConfig` JSON config changes and validation checklist | ## Observability diff --git a/docs/guides/migrating-json-jackson-core-to-avaje-json-core.md b/docs/guides/migrating-json-jackson-core-to-avaje-json-core.md new file mode 100644 index 000000000..da7fbc218 --- /dev/null +++ b/docs/guides/migrating-json-jackson-core-to-avaje-json-core.md @@ -0,0 +1,91 @@ +# Guide: Migrate JSON APIs from Jackson core to avaje-json-core + +## Purpose + +This guide covers the one-step cutover in Ebean from Jackson core JSON APIs to +avaje-json-core APIs. + +Use this when upgrading code that references: + +- `com.fasterxml.jackson.core.JsonParser` +- `com.fasterxml.jackson.core.JsonGenerator` +- `com.fasterxml.jackson.core.JsonFactory` + +The replacement types are: + +- `io.avaje.json.JsonReader` +- `io.avaje.json.JsonWriter` +- `io.avaje.json.stream.JsonStream` + +--- + +## Breaking API changes + +| Previous API | New API | +|---|---| +| `JsonParser` | `JsonReader` | +| `JsonGenerator` | `JsonWriter` | +| `JsonFactory` | `JsonStream` | +| `DatabaseBuilder.jsonFactory(...)` | `DatabaseBuilder.jsonStream(...)` | +| `DatabaseConfig.getJsonFactory()/setJsonFactory(...)` | `DatabaseConfig.getJsonStream()/setJsonStream(...)` | + +--- + +## Typical migration rewrites + +### Parser and generator signatures + +```java +// before +void read(JsonParser parser) +void write(JsonGenerator generator) + +// after +void read(JsonReader parser) +void write(JsonWriter generator) +``` + +### Database configuration + +```java +// before +Database.builder().jsonFactory(factory) + +// after +Database.builder().jsonStream(stream) +``` + +### JSON utility calls + +`EJson` and `JsonContext` APIs now operate on `JsonReader` and `JsonWriter` types. +If your code was calling those APIs with Jackson core types, switch to avaje types. + +--- + +## Dependency and module notes + +- `ebean-core` no longer requires a direct `jackson-core` dependency for JSON + parsing/writing. +- `jackson-databind` remains optional for `ObjectMapper` compatibility paths. +- `ebean-jackson-mapper` remains the compatibility bridge module for mapper-based + integrations. + +--- + +## Behavior notes to verify during upgrade + +1. Parser token handling is now based on avaje `JsonReader.Token`. +2. Scalar JSON reads (for example booleans, date-time, array scalar types) should + be validated in your tests if you previously depended on Jackson token quirks. +3. If your integration uses transient assoc-many JSON mapping with ObjectMapper, + keep ObjectMapper wiring enabled. + +--- + +## Validation checklist + +1. Compile all modules that implement or consume `io.ebean.text.json` APIs. +2. Run module tests that cover JSON scalar conversion and bean JSON round-trips. +3. Confirm no remaining `com.fasterxml.jackson.core.*` imports in migrated code. +4. Keep `ObjectMapper` compatibility tests if your project depends on mapper paths. + diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index e7435fbdc..5d7b43c81 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -70,15 +70,13 @@ true - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - true + io.avaje + avaje-json-core + ${avaje-json-core.version} - + com.fasterxml.jackson.core jackson-databind diff --git a/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java b/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java index 0f1c6eeeb..51e6f9618 100644 --- a/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java +++ b/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java @@ -1,6 +1,6 @@ package io.ebean; -import com.fasterxml.jackson.core.JsonFactory; +import io.avaje.json.stream.JsonStream; import io.ebean.annotation.*; import io.ebean.cache.ServerCachePlugin; import io.ebean.config.*; @@ -360,18 +360,18 @@ public interface DatabaseBuilder { DatabaseBuilder putServiceObject(Object configObject); /** - * Set the Jackson JsonFactory to use. + * Set the JsonStream to use. *

* If not set a default implementation will be used. */ - default DatabaseBuilder jsonFactory(JsonFactory jsonFactory) { - return setJsonFactory(jsonFactory); + default DatabaseBuilder jsonStream(JsonStream jsonStream) { + return setJsonStream(jsonStream); } /** - * @deprecated migrate to {@link #jsonFactory(JsonFactory)}. + * @deprecated migrate to {@link #jsonStream(JsonStream)}. */ - DatabaseBuilder setJsonFactory(JsonFactory jsonFactory); + DatabaseBuilder setJsonStream(JsonStream jsonStream); /** * Set the JSON format to use for DateTime types. @@ -2254,11 +2254,11 @@ public interface DatabaseBuilder { boolean isAutoLoadModuleInfo(); /** - * Return the Jackson JsonFactory to use. + * Return the JsonStream to use. *

* If not set a default implementation will be used. */ - JsonFactory getJsonFactory(); + JsonStream getJsonStream(); /** * Get the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects. diff --git a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java index 7fa749ca8..8a1bf02ab 100644 --- a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java @@ -60,7 +60,8 @@ public class ClassLoadConfig { } public boolean isJacksonCorePresent() { - return isPresent("com.fasterxml.jackson.core.JsonParser"); + // Legacy method name retained for compatibility; now checks avaje JSON core. + return isPresent("io.avaje.json.JsonReader"); } /** @@ -158,4 +159,3 @@ public class ClassLoadConfig { } } } - diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index 3c3624573..33dbf399f 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -1,7 +1,7 @@ package io.ebean.config; -import com.fasterxml.jackson.core.JsonFactory; import io.avaje.config.Config; +import io.avaje.json.stream.JsonStream; import io.ebean.*; import io.ebean.annotation.MutationDetection; import io.ebean.annotation.PersistBatch; @@ -420,7 +420,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings { * The default PersistenceContextScope used if one is not explicitly set on a query. */ private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION; - private JsonFactory jsonFactory; + private JsonStream jsonStream; private boolean localTimeWithNanos; private boolean durationWithNanos; private int maxCallStack = 5; @@ -631,13 +631,13 @@ public class DatabaseConfig implements DatabaseBuilder.Settings { } @Override - public JsonFactory getJsonFactory() { - return jsonFactory; + public JsonStream getJsonStream() { + return jsonStream; } @Override - public DatabaseConfig setJsonFactory(JsonFactory jsonFactory) { - this.jsonFactory = jsonFactory; + public DatabaseConfig setJsonStream(JsonStream jsonStream) { + this.jsonStream = jsonStream; return this; } diff --git a/ebean-api/src/main/java/io/ebean/service/SpiJsonService.java b/ebean-api/src/main/java/io/ebean/service/SpiJsonService.java index a7fb1753d..25eb68a95 100644 --- a/ebean-api/src/main/java/io/ebean/service/SpiJsonService.java +++ b/ebean-api/src/main/java/io/ebean/service/SpiJsonService.java @@ -1,8 +1,8 @@ package io.ebean.service; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; import java.io.IOException; import java.io.Reader; @@ -32,12 +32,12 @@ public interface SpiJsonService extends BootstrapService { /** * Write the nested Map/List as json to the jsonGenerator. */ - void write(Object object, JsonGenerator jsonGenerator) throws IOException; + void write(Object object, JsonWriter jsonGenerator) throws IOException; /** * Write the collection as json array to the jsonGenerator. */ - void writeCollection(Collection collection, JsonGenerator jsonGenerator) throws IOException; + void writeCollection(Collection collection, JsonWriter jsonGenerator) throws IOException; /** * Parse the json and return as a Map additionally specifying if the returned map should @@ -61,17 +61,17 @@ public interface SpiJsonService extends BootstrapService { Map parseObject(Reader reader) throws IOException; /** - * Parse the json and return as a Map taking a JsonParser. + * Parse the json and return as a Map taking a JsonReader. */ - Map parseObject(JsonParser parser) throws IOException; + Map parseObject(JsonReader parser) throws IOException; /** - * Parse the json and return as a Map taking a JsonParser and a starting token. + * Parse the json and return as a Map taking a JsonReader and a starting token. *

* Used when the first token is checked to see if the value is null prior to calling this. *

*/ - Map parseObject(JsonParser parser, JsonToken token) throws IOException; + Map parseObject(JsonReader parser, Token token) throws IOException; /** * Parse the json and return as a modify aware List. @@ -89,14 +89,14 @@ public interface SpiJsonService extends BootstrapService { List parseList(Reader reader) throws IOException; /** - * Parse the json and return as a List taking a JsonParser. + * Parse the json and return as a List taking a JsonReader. */ - List parseList(JsonParser parser) throws IOException; + List parseList(JsonReader parser) throws IOException; /** * Parse the json returning as a List taking into account the current token. */ - List parseList(JsonParser parser, JsonToken currentToken) throws IOException; + List parseList(JsonReader parser, Token currentToken) throws IOException; /** * Parse the json and return as a List or Map. @@ -111,7 +111,7 @@ public interface SpiJsonService extends BootstrapService { /** * Parse the json and return as a List or Map. */ - Object parse(JsonParser parser) throws IOException; + Object parse(JsonReader parser) throws IOException; /** * Parse the json returning a Set that might be modify aware. @@ -121,5 +121,5 @@ public interface SpiJsonService extends BootstrapService { /** * Parse the json returning as a Set taking into account the current token. */ - Set parseSet(JsonParser parser, JsonToken currentToken) throws IOException; + Set parseSet(JsonReader parser, Token currentToken) throws IOException; } diff --git a/ebean-api/src/main/java/io/ebean/text/json/EJson.java b/ebean-api/src/main/java/io/ebean/text/json/EJson.java index e1aabd701..61fe6a6a2 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/EJson.java +++ b/ebean-api/src/main/java/io/ebean/text/json/EJson.java @@ -1,8 +1,7 @@ package io.ebean.text.json; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.XBootstrapService; import io.ebean.service.SpiJsonService; @@ -38,14 +37,14 @@ public class EJson { /** * Write the nested Map/List as json to the jsonGenerator. */ - public static void write(Object object, JsonGenerator jsonGenerator) throws IOException { + public static void write(Object object, io.avaje.json.JsonWriter jsonGenerator) throws IOException { plugin.write(object, jsonGenerator); } /** * Write the collection as json array to the jsonGenerator. */ - public static void writeCollection(Collection collection, JsonGenerator jsonGenerator) throws IOException { + public static void writeCollection(Collection collection, io.avaje.json.JsonWriter jsonGenerator) throws IOException { plugin.writeCollection(collection, jsonGenerator); } @@ -79,19 +78,19 @@ public class EJson { } /** - * Parse the json and return as a Map taking a JsonParser. + * Parse the json and return as a Map taking a JsonReader. */ - public static Map parseObject(JsonParser parser) throws IOException { + public static Map parseObject(JsonReader parser) throws IOException { return plugin.parseObject(parser); } /** - * Parse the json and return as a Map taking a JsonParser and a starting token. + * Parse the json and return as a Map taking a JsonReader and a starting token. *

* Used when the first token is checked to see if the value is null prior to calling this. *

*/ - public static Map parseObject(JsonParser parser, JsonToken token) throws IOException { + public static Map parseObject(JsonReader parser, Token token) throws IOException { return plugin.parseObject(parser, token); } @@ -117,16 +116,16 @@ public class EJson { } /** - * Parse the json and return as a List taking a JsonParser. + * Parse the json and return as a List taking a JsonReader. */ - public static List parseList(JsonParser parser) throws IOException { + public static List parseList(JsonReader parser) throws IOException { return plugin.parseList(parser); } /** * Parse the json returning as a List taking into account the current token. */ - public static List parseList(JsonParser parser, JsonToken currentToken) throws IOException { + public static List parseList(JsonReader parser, Token currentToken) throws IOException { return plugin.parseList(parser, currentToken); } @@ -147,7 +146,7 @@ public class EJson { /** * Parse the json and return as a List or Map. */ - public static Object parse(JsonParser parser) throws IOException { + public static Object parse(JsonReader parser) throws IOException { return plugin.parse(parser); } @@ -161,7 +160,7 @@ public class EJson { /** * Parse the json returning as a Set taking into account the current token. */ - public static Set parseSet(JsonParser parser, JsonToken currentToken) throws IOException { + public static Set parseSet(JsonReader parser, Token currentToken) throws IOException { return plugin.parseSet(parser, currentToken); } } diff --git a/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java b/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java index 935b78860..ddc3c4bce 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java +++ b/ebean-api/src/main/java/io/ebean/text/json/JsonBeanReader.java @@ -1,6 +1,6 @@ package io.ebean.text.json; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.bean.PersistenceContext; /** @@ -25,9 +25,9 @@ public interface JsonBeanReader { } /** - * Create a new reader taking the context from the existing one but using a new JsonParser. + * Create a new reader taking the context from the existing one but using a new JsonReader. */ - JsonBeanReader forJson(JsonParser moreJson); + JsonBeanReader forJson(JsonReader moreJson); /** * Add a bean explicitly to the persistence context. diff --git a/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java b/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java index d2c0df822..0c9925f5f 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java +++ b/ebean-api/src/main/java/io/ebean/text/json/JsonContext.java @@ -1,7 +1,6 @@ package io.ebean.text.json; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.FetchPath; import io.ebean.plugin.BeanType; @@ -49,14 +48,14 @@ public interface JsonContext { * * @throws JsonIOException When IOException occurs */ - T toBean(Class cls, JsonParser parser) throws JsonIOException; + T toBean(Class cls, JsonReader parser) throws JsonIOException; /** * Convert json parser input into a Bean of a specific type additionally using JsonReadOptions.. * * @throws JsonIOException When IOException occurs */ - T toBean(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException; + T toBean(Class cls, JsonReader parser, JsonReadOptions options) throws JsonIOException; /** * Read json parser input into a given Bean.
@@ -65,19 +64,19 @@ public interface JsonContext { * * @throws JsonIOException When IOException occurs */ - void toBean(T target, JsonParser parser) throws JsonIOException; + void toBean(T target, JsonReader parser) throws JsonIOException; /** * Read json parser input into a given Bean additionally using JsonReadOptions.
- * See {@link #toBean(Class, JsonParser)} for details modified. + * See {@link #toBean(Class, JsonReader)} for details modified. * * @throws JsonIOException When IOException occurs */ - void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException; + void toBean(T target, JsonReader parser, JsonReadOptions options) throws JsonIOException; /** * Read json reader input into a given Bean.
- * See {@link #toBean(Class, JsonParser)} for details + * See {@link #toBean(Class, JsonReader)} for details * * @throws JsonIOException When IOException occurs */ @@ -85,7 +84,7 @@ public interface JsonContext { /** * Read json reader input into a given Bean additionally using JsonReadOptions.
- * See {@link #toBean(Class, JsonParser)} for details modified. + * See {@link #toBean(Class, JsonReader)} for details modified. * * @throws JsonIOException When IOException occurs */ @@ -93,7 +92,7 @@ public interface JsonContext { /** * Read json string input into a given Bean.
- * See {@link #toBean(Class, JsonParser)} for details + * See {@link #toBean(Class, JsonReader)} for details * * @throws JsonIOException When IOException occurs */ @@ -101,7 +100,7 @@ public interface JsonContext { /** * Read json string input into a given Bean additionally using JsonReadOptions.
- * See {@link #toBean(Class, JsonParser)} for details + * See {@link #toBean(Class, JsonReader)} for details * * @throws JsonIOException When IOException occurs */ @@ -113,7 +112,7 @@ public interface JsonContext { * Note that JsonOption provides an option for setting a persistence context and also enabling further lazy loading. Further lazy * loading requires a persistence context so if that is set on then a persistence context is created if there is not one set. */ - JsonBeanReader createBeanReader(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException; + JsonBeanReader createBeanReader(Class cls, JsonReader parser, JsonReadOptions options) throws JsonIOException; /** * Create and return a new bean reading for the bean type given the JSON options and source. @@ -122,7 +121,7 @@ public interface JsonContext { * further lazy loading. Further lazy loading requires a persistence context so if that is set * on then a persistence context is created if there is not one set. */ - JsonBeanReader createBeanReader(BeanType beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException; + JsonBeanReader createBeanReader(BeanType beanType, JsonReader parser, JsonReadOptions options) throws JsonIOException; /** * Convert json string input into a list of beans of a specific type. @@ -157,14 +156,14 @@ public interface JsonContext { * * @throws JsonIOException When IOException occurs */ - List toList(Class cls, JsonParser json) throws JsonIOException; + List toList(Class cls, JsonReader json) throws JsonIOException; /** * Convert json parser input into a list of beans of a specific type additionally using JsonReadOptions. * * @throws JsonIOException When IOException occurs */ - List toList(Class cls, JsonParser json, JsonReadOptions options) throws JsonIOException; + List toList(Class cls, JsonReader json, JsonReadOptions options) throws JsonIOException; /** * Use the genericType to determine if this should be converted into a List or @@ -188,7 +187,7 @@ public interface JsonContext { * * @throws JsonIOException When IOException occurs */ - Object toObject(Type genericType, JsonParser jsonParser) throws JsonIOException; + Object toObject(Type genericType, JsonReader jsonParser) throws JsonIOException; /** * Return the bean or collection as JSON string. @@ -212,11 +211,11 @@ public interface JsonContext { void toJson(Object value, Writer writer) throws JsonIOException; /** - * Write the bean or collection to the JsonGenerator. + * Write the bean or collection to the JsonWriter. * * @throws JsonIOException When IOException occurs */ - void toJson(Object value, JsonGenerator generator) throws JsonIOException; + void toJson(Object value, io.avaje.json.JsonWriter generator) throws JsonIOException; /** * Return the bean or collection as JSON string using FetchPath. @@ -231,15 +230,15 @@ public interface JsonContext { void toJson(Object value, Writer writer, FetchPath fetchPath) throws JsonIOException; /** - * Write the bean or collection to the JsonGenerator using the FetchPath. + * Write the bean or collection to the JsonWriter using the FetchPath. */ - void toJson(Object value, JsonGenerator generator, FetchPath fetchPath) throws JsonIOException; + void toJson(Object value, io.avaje.json.JsonWriter generator, FetchPath fetchPath) throws JsonIOException; /** * Deprecated in favour of using PathProperties by itself. - * Write json to the JsonGenerator using the JsonWriteOptions. + * Write json to the JsonWriter using the JsonWriteOptions. */ - void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException; + void toJson(Object value, io.avaje.json.JsonWriter generator, JsonWriteOptions options) throws JsonIOException; /** * Deprecated in favour of using PathProperties by itself. @@ -264,27 +263,27 @@ public interface JsonContext { boolean isSupportedType(Type genericType); /** - * Create and return a new JsonGenerator for the given writer. + * Create and return a new JsonWriter for the given writer. * * @throws JsonIOException When IOException occurs */ - JsonGenerator createGenerator(Writer writer) throws JsonIOException; + io.avaje.json.JsonWriter createGenerator(Writer writer) throws JsonIOException; /** - * Create and return a new JsonParser for the given reader. + * Create and return a new JsonReader for the given reader. * * @throws JsonIOException When IOException occurs */ - JsonParser createParser(Reader reader) throws JsonIOException; + JsonReader createParser(Reader reader) throws JsonIOException; /** - * Write a scalar types known to Ebean to Jackson. + * Write scalar types known to Ebean to JsonWriter. *

* Ebean has built in support for java8 and Joda types as well as the other * standard JDK types like URI, URL, UUID etc. This is a fast simple way to - * write any of those types to Jackson. + * write any of those types. *

*/ - void writeScalar(JsonGenerator generator, Object scalarValue) throws IOException; + void writeScalar(io.avaje.json.JsonWriter generator, Object scalarValue) throws IOException; } diff --git a/ebean-api/src/main/java/io/ebean/text/json/JsonWriter.java b/ebean-api/src/main/java/io/ebean/text/json/JsonWriter.java index baed9a9cb..fd895e9a9 100644 --- a/ebean-api/src/main/java/io/ebean/text/json/JsonWriter.java +++ b/ebean-api/src/main/java/io/ebean/text/json/JsonWriter.java @@ -1,19 +1,17 @@ package io.ebean.text.json; -import com.fasterxml.jackson.core.JsonGenerator; - import java.io.InputStream; import java.math.BigDecimal; /** - * Wraps an underlying JsonGenerator taking into account null suppression and exposing isIncludeEmpty() etc. + * Wraps an underlying JsonWriter taking into account null suppression and exposing isIncludeEmpty() etc. */ public interface JsonWriter { /** - * Return the Jackson core JsonGenerator. + * Return the underlying JsonWriter. */ - JsonGenerator gen(); + io.avaje.json.JsonWriter gen(); /** * Return true if null values should be included in JSON output. diff --git a/ebean-api/src/main/java/module-info.java b/ebean-api/src/main/java/module-info.java index 1768da278..abdc78ff8 100644 --- a/ebean-api/src/main/java/module-info.java +++ b/ebean-api/src/main/java/module-info.java @@ -8,6 +8,7 @@ module io.ebean.api { requires transitive java.sql; requires transitive io.avaje.config; + requires transitive io.avaje.json; requires transitive org.jspecify; requires transitive jakarta.persistence.api; requires transitive io.ebean.annotation; @@ -16,7 +17,6 @@ module io.ebean.api { requires static org.slf4j; requires static io.ebean.types; - requires static com.fasterxml.jackson.core; requires static com.fasterxml.jackson.databind; exports io.ebean; diff --git a/ebean-core-json/pom.xml b/ebean-core-json/pom.xml index 69732b20f..6af5fb9d0 100644 --- a/ebean-core-json/pom.xml +++ b/ebean-core-json/pom.xml @@ -19,12 +19,10 @@ 16.11.0 - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - true + io.avaje + avaje-json-core + ${avaje-json-core.version} diff --git a/ebean-core-json/src/main/java/io/ebeaninternal/json/DJsonService.java b/ebean-core-json/src/main/java/io/ebeaninternal/json/DJsonService.java index f286bae4d..cbec38cd5 100644 --- a/ebean-core-json/src/main/java/io/ebeaninternal/json/DJsonService.java +++ b/ebean-core-json/src/main/java/io/ebeaninternal/json/DJsonService.java @@ -1,190 +1,129 @@ package io.ebeaninternal.json; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; import io.ebean.service.SpiJsonService; import java.io.IOException; import java.io.Reader; import java.io.Writer; -import java.util.*; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Utility that converts between JSON content and simple java Maps/Lists. */ public final class DJsonService implements SpiJsonService { - /** - * Write the nested Map/List as json. - */ @Override public String write(Object object) throws IOException { return EJsonWriter.write(object); } - /** - * Write the nested Map/List as json to the writer. - */ @Override public void write(Object object, Writer writer) throws IOException { EJsonWriter.write(object, writer); } - /** - * Write the nested Map/List as json to the jsonGenerator. - */ @Override - public void write(Object object, JsonGenerator jsonGenerator) throws IOException { - EJsonWriter.write(object, jsonGenerator); + public void write(Object object, JsonWriter jsonWriter) throws IOException { + EJsonWriter.write(object, jsonWriter); } - /** - * Write the collection as json array to the jsonGenerator. - */ @Override - public void writeCollection(Collection collection, JsonGenerator jsonGenerator) throws IOException { - EJsonWriter.writeCollection(collection, jsonGenerator); + public void writeCollection(Collection collection, JsonWriter jsonWriter) throws IOException { + EJsonWriter.writeCollection(collection, jsonWriter); } - /** - * Parse the json and return as a Map additionally specifying if the returned map should be modify - * aware meaning that it can detect when it has been modified. - */ @Override public Map parseObject(String json, boolean modifyAware) throws IOException { return EJsonReader.parseObject(json, modifyAware); } - /** - * Parse the json and return as a Map. - */ @Override public Map parseObject(String json) throws IOException { return EJsonReader.parseObject(json); } - /** - * Parse the json and return as a Map taking a reader. - */ @Override public Map parseObject(Reader reader, boolean modifyAware) throws IOException { return EJsonReader.parseObject(reader, modifyAware); } - /** - * Parse the json and return as a Map taking a reader. - */ @Override public Map parseObject(Reader reader) throws IOException { return EJsonReader.parseObject(reader); } - /** - * Parse the json and return as a Map taking a JsonParser. - */ @Override - public Map parseObject(JsonParser parser) throws IOException { + public Map parseObject(JsonReader parser) throws IOException { return EJsonReader.parseObject(parser); } - /** - * Parse the json and return as a Map taking a JsonParser and a starting token. - * - *

Used when the first token is checked to see if the value is null prior to calling this. - */ @Override - public Map parseObject(JsonParser parser, JsonToken token) throws IOException { + public Map parseObject(JsonReader parser, Token token) throws IOException { return EJsonReader.parseObject(parser, token); } - /** - * Parse the json and return as a modify aware List. - */ @Override public List parseList(String json, boolean modifyAware) throws IOException { return EJsonReader.parseList(json, modifyAware); } - /** - * Parse the json and return as a List. - */ @Override public List parseList(String json) throws IOException { return EJsonReader.parseList(json); } - /** - * Parse the json and return as a List taking a Reader. - */ @Override public List parseList(Reader reader) throws IOException { return EJsonReader.parseList(reader); } - /** - * Parse the json and return as a List taking a JsonParser. - */ @Override - public List parseList(JsonParser parser) throws IOException { + public List parseList(JsonReader parser) throws IOException { return EJsonReader.parseList(parser, false); } - /** - * Parse the json returning as a List taking into account the current token. - */ @Override @SuppressWarnings("unchecked") - public List parseList(JsonParser parser, JsonToken currentToken) throws IOException { + public List parseList(JsonReader parser, Token currentToken) throws IOException { return (List) EJsonReader.parse(parser, currentToken, false); } - /** - * Parse the json and return as a List or Map. - */ @Override public Object parse(String json) throws IOException { return EJsonReader.parse(json); } - /** - * Parse the json and return as a List or Map. - */ @Override public Object parse(Reader reader) throws IOException { return EJsonReader.parse(reader); } - /** - * Parse the json and return as a List or Map. - */ @Override - public Object parse(JsonParser parser) throws IOException { + public Object parse(JsonReader parser) throws IOException { return EJsonReader.parse(parser); } - /** - * Parse the json returning a Set that might be modify aware. - */ @Override public Set parseSet(String json, boolean modifyAware) throws IOException { List list = parseList(json, modifyAware); if (list == null) { return null; } - if (modifyAware) { return ((ModifyAwareList) list).asSet(); - } else { - return new LinkedHashSet<>(list); } + return new LinkedHashSet<>(list); } - /** - * Parse the json returning as a Set taking into account the current token. - */ @Override - public Set parseSet(JsonParser parser, JsonToken currentToken) throws IOException { + public Set parseSet(JsonReader parser, Token currentToken) throws IOException { return new LinkedHashSet<>(parseList(parser, currentToken)); } } diff --git a/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonReader.java b/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonReader.java index 7c808979f..1d40f8f24 100644 --- a/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonReader.java +++ b/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonReader.java @@ -1,29 +1,25 @@ package io.ebeaninternal.json; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.stream.JsonStream; import io.ebean.ModifyAwareType; import java.io.IOException; import java.io.Reader; -import java.io.StringReader; -import java.util.*; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; final class EJsonReader { - static final JsonFactory json = new JsonFactory(); - private final JsonParser parser; - private final boolean modifyAware; - private final ModifyAwareFlag modifyAwareOwner; - private int depth; - private Stack stack; - private Context currentContext; + private EJsonReader() { + } - EJsonReader(JsonParser parser, boolean modifyAware) { - this.parser = parser; - this.modifyAware = modifyAware; - this.modifyAwareOwner = modifyAware ? new ModifyAwareFlag() : null; + private static JsonReader reader(String content) { + return JsonStream.builder().build().reader(content); } @SuppressWarnings("unchecked") @@ -47,12 +43,12 @@ final class EJsonReader { } @SuppressWarnings("unchecked") - static Map parseObject(JsonParser parser) throws IOException { + static Map parseObject(JsonReader parser) throws IOException { return (Map) parse(parser); } @SuppressWarnings("unchecked") - static Map parseObject(JsonParser parser, JsonToken token) throws IOException { + static Map parseObject(JsonReader parser, Token token) throws IOException { return (Map) parse(parser, token, false); } @@ -72,285 +68,137 @@ final class EJsonReader { } @SuppressWarnings("unchecked") - static List parseList(JsonParser parser, boolean modifyAware) throws IOException { - return (List) parse(parser, modifyAware); + static List parseList(JsonReader parser, boolean modifyAware) throws IOException { + return (List) parse(parser, null, modifyAware); } static Object parse(String json) throws IOException { - if (json == null) { - return null; - } - return parse(new StringReader(json)); + return parseRawJson(json, null); } static Object parse(String json, boolean modifyAware) throws IOException { - if (json == null) { - return null; - } - return parse(new StringReader(json), modifyAware); + return parseRawJson(json, modifyAware ? new ModifyAwareFlag() : null); } static Object parse(Reader reader) throws IOException { - return parse(json.createParser(reader)); + return parseRawJson(readAll(reader), null); } static Object parse(Reader reader, boolean modifyAware) throws IOException { - return parse(json.createParser(reader), modifyAware); + return parseRawJson(readAll(reader), modifyAware ? new ModifyAwareFlag() : null); } - static Object parse(JsonParser parser) throws IOException { + static Object parse(JsonReader parser) throws IOException { return parse(parser, null, false); } - static Object parse(JsonParser parser, boolean modifyAware) throws IOException { + static Object parse(JsonReader parser, boolean modifyAware) throws IOException { return parse(parser, null, modifyAware); } - static Object parse(JsonParser parser, JsonToken token, boolean modifyAware) throws IOException { - return new EJsonReader(parser, modifyAware).parseJson(token); - } - - private void startArray() { - depth++; - stack.push(currentContext); - currentContext = modifyAware ? new ArrayContext(modifyAwareOwner) : new ArrayContext(); - } - - private void startObject() { - depth++; - stack.push(currentContext); - currentContext = modifyAware ? new ObjectContext(modifyAwareOwner) : new ObjectContext(); - } - - private void endArray() { - end(); - } - - private void endObject() { - end(); - } - - private void end() { - depth--; - if (!stack.isEmpty()) { - currentContext = stack.pop(currentContext); - } - if (modifyAwareOwner != null) { - modifyAwareOwner.setMarkedDirty(false); + static Object parse(JsonReader parser, Token token, boolean modifyAware) throws IOException { + ModifyAwareType owner = modifyAware ? new ModifyAwareFlag() : null; + Token effectiveToken = token == null ? parser.currentToken() : token; + if (effectiveToken == null) { + return parseRawJson(parser.readRaw(), owner); } + return parseValue(parser, effectiveToken, owner); } - private void setValue(Object value) { - currentContext.setValue(value); - } - - private void setValueNull() { - currentContext.setValueNull(); - } - - private Object parseJson(JsonToken token) throws IOException { - + private static Object parseValue(JsonReader parser, Token token, ModifyAwareType owner) throws IOException { if (token == null) { - token = parser.nextToken(); - // if it is a simple value just return it - switch (token) { - case VALUE_NULL: + token = parser.currentToken(); + if (token == null) { + if (parser.isNullValue()) { return null; - case VALUE_FALSE: - return Boolean.FALSE; - case VALUE_TRUE: - return Boolean.TRUE; - case VALUE_STRING: - return parser.getText(); - case VALUE_NUMBER_INT: - return parser.getLongValue(); - case VALUE_NUMBER_FLOAT: - return parser.getDecimalValue(); + } + return parseRawJson(parser.readRaw(), owner); } } - - // it is a object or array, process the first JsonToken - stack = new Stack(); - processJsonToken(token); - - // process the rest of the object or array - while (depth > 0) { - token = parser.nextToken(); - processJsonToken(token); + if (token == Token.BEGIN_OBJECT) { + return parseObjectValue(parser, owner); } - - return currentContext.getValue(); + if (token == Token.BEGIN_ARRAY) { + return parseArrayValue(parser, owner); + } + if (token == Token.NUMBER) { + BigDecimal value = parser.readDecimal(); + return value.scale() <= 0 ? value.longValue() : value; + } + if (token == Token.STRING) { + return parser.readString(); + } + if (token == Token.BOOLEAN) { + return parser.readBoolean(); + } + if (token == Token.NULL) { + parser.isNullValue(); + return null; + } + return parseRawJson(parser.readRaw(), owner); } - /** - * Process the JsonToken for objects and arrays. - */ - private void processJsonToken(JsonToken token) throws IOException { - switch (token) { - case START_ARRAY: - startArray(); - break; - - case START_OBJECT: - startObject(); - break; - - case FIELD_NAME: - currentContext.setKey(parser.getCurrentName()); - break; - - case VALUE_STRING: - setValue(parser.getValueAsString()); - break; - - case VALUE_NUMBER_INT: - setValue(parser.getLongValue()); - break; - - case VALUE_NUMBER_FLOAT: - setValue(parser.getDecimalValue()); - break; - - case VALUE_TRUE: - setValue(Boolean.TRUE); - break; - - case VALUE_FALSE: - setValue(Boolean.FALSE); - break; - - case VALUE_NULL: - setValueNull(); - break; - - case END_OBJECT: - endObject(); - break; - - case END_ARRAY: - endArray(); - break; - - default: - break; + private static Object parseRawJson(String json, ModifyAwareType owner) throws IOException { + if (json == null) { + return null; } + String content = json.trim(); + if (content.isEmpty()) { + return null; + } + JsonReader parser = reader(content); + Token token = parser.currentToken(); + return parseValue(parser, token, owner); } - private static final class Stack { + private static String readAll(Reader reader) throws IOException { + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[2048]; + int len; + while ((len = reader.read(buffer)) != -1) { + builder.append(buffer, 0, len); + } + return builder.toString(); + } - private Context head; + private static Map parseObjectValue(JsonReader parser, ModifyAwareType owner) throws IOException { + Map map = owner == null + ? new LinkedHashMap<>() + : new ModifyAwareMap<>(owner, new LinkedHashMap<>()); - private void push(Context context) { - if (context != null) { - context.next = head; - head = context; + parser.beginObject(); + while (parser.hasNextField()) { + String fieldName = parser.nextField(); + map.put(fieldName, parseValue(parser, parser.currentToken(), owner)); + if (owner != null) { + owner.setMarkedDirty(false); } } + parser.endObject(); + if (owner != null) { + owner.setMarkedDirty(false); + } + return map; + } - private Context pop(Context endingContext) { - if (head == null) { - throw new NoSuchElementException(); + private static List parseArrayValue(JsonReader parser, ModifyAwareType owner) throws IOException { + List list = owner == null + ? new ArrayList<>() + : new ModifyAwareList<>(owner, new ArrayList<>()); + + parser.beginArray(); + while (parser.hasNextElement()) { + Token elementToken = parser.currentToken(); + Object elementValue = parseValue(parser, elementToken, owner); + list.add(elementValue); + if (owner != null) { + owner.setMarkedDirty(false); } - Context temp = head; - head = head.next; - temp.popContext(endingContext); - return temp; } - - private boolean isEmpty() { - return head == null; - } - } - - private abstract static class Context { - Context next; - - abstract void popContext(Context temp); - - abstract Object getValue(); - - abstract void setValue(Object value); - - abstract void setKey(String key); - - abstract void setValueNull(); - } - - private static class ObjectContext extends Context { - - private final Map map; - - private String key; - - ObjectContext() { - map = new LinkedHashMap<>(); - } - - ObjectContext(ModifyAwareType owner) { - map = new ModifyAwareMap<>(owner, new LinkedHashMap<>()); - } - - @Override - public void popContext(Context temp) { - setValue(temp.getValue()); - } - - @Override - Object getValue() { - return map; - } - - @Override - void setValue(Object value) { - map.put(key, value); - } - - @Override - void setKey(String key) { - this.key = key; - } - - @Override - void setValueNull() { - map.put(key, null); - } - } - - private static class ArrayContext extends Context { - - private final List values; - - ArrayContext() { - values = new ArrayList<>(); - } - - ArrayContext(ModifyAwareType owner) { - values = new ModifyAwareList<>(owner, new ArrayList<>()); - } - - @Override - public void popContext(Context temp) { - values.add(temp.getValue()); - } - - @Override - Object getValue() { - return values; - } - - @Override - void setValue(Object value) { - values.add(value); - } - - @Override - void setValueNull() { - // ignore - } - - @Override - void setKey(String key) { - // not expected + parser.endArray(); + if (owner != null) { + owner.setMarkedDirty(false); } + return list; } } diff --git a/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonWriter.java b/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonWriter.java index ff73de8f9..46c7f4434 100644 --- a/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonWriter.java +++ b/ebean-core-json/src/main/java/io/ebeaninternal/json/EJsonWriter.java @@ -1,212 +1,149 @@ package io.ebeaninternal.json; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; +import io.avaje.json.stream.JsonStream; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; -import java.math.BigInteger; import java.util.Collection; -import java.util.Date; +import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; +/** + * Utility to write simple java Maps/Lists as JSON. + */ final class EJsonWriter { - /** - * Base jsonFactory implementation used when it is not passed in. - */ - static final JsonFactory jsonFactory = new JsonFactory(); - private final JsonGenerator jsonGenerator; + private static final JsonStream JSON_STREAM = JsonStream.builder().build(); - private EJsonWriter(JsonGenerator jsonGenerator) { - this.jsonGenerator = jsonGenerator; + private EJsonWriter() { } + /** + * Convert object to Json content. + */ static String write(Object object) throws IOException { - StringWriter writer = new StringWriter(200); - write(object, writer).close(); + StringWriter writer = new StringWriter(); + write(object, writer); return writer.toString(); } - static JsonGenerator write(Object object, Writer writer) throws IOException { - JsonGenerator generator = jsonFactory.createGenerator(writer); - write(object, generator); - generator.flush(); - return generator; + /** + * Convert object to Json content. + */ + static io.avaje.json.JsonWriter write(Object object, Writer writer) throws IOException { + io.avaje.json.JsonWriter jsonWriter = JSON_STREAM.writer(writer); + jsonWriter.serializeNulls(true); + write(object, jsonWriter); + jsonWriter.flush(); + return jsonWriter; } - static void write(Object object, JsonGenerator jsonGenerator) { - new EJsonWriter(jsonGenerator).writeJson(object); - } - - static void writeCollection(Collection collection, JsonGenerator jsonGenerator) throws IOException { - new EJsonWriter(jsonGenerator).writeCollection(null, collection); - } - - private void writeJson(Object object) { - writeJson(null, object); - } - - @SuppressWarnings("unchecked") - private void writeJson(String name, Object object) { - try { - if (object == null) { - writeNull(name); - - } else if (object instanceof Number) { - writeNumber(name, (Number) object); - - } else if (object instanceof String) { - writeString(name, (String) object); - - } else if (object instanceof Map) { - writeMap(name, (Map) object); - - } else if (object instanceof Collection) { - writeCollection(name, (Collection) object); - - } else if (object instanceof Boolean) { - writeBoolean(name, (Boolean) object); - - } else if (object instanceof Date) { - writeDate(name, (Date) object); - - } else if (object instanceof Map.Entry) { - Map.Entry entry = (Map.Entry) object; - writeJson(entry.getKey().toString(), entry.getValue()); - - } else { - writeString(name, object.toString()); - } - - } catch (IOException e) { - throw new RuntimeException(e); + /** + * Convert object to Json content. + */ + static void write(Object object, io.avaje.json.JsonWriter jsonWriter) throws IOException { + if (object == null) { + jsonWriter.nullValue(); + return; } - } - - private void writeBoolean(String name, Boolean object) throws IOException { - if (name == null) { - jsonGenerator.writeBoolean(object); - } else { - jsonGenerator.writeBooleanField(name, object); + if (object instanceof String) { + jsonWriter.value((String) object); + return; } - } - - private void writeDate(String name, Date object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber(object.getTime()); - } else { - jsonGenerator.writeNumberField(name, object.getTime()); + if (object instanceof Integer) { + jsonWriter.value((Integer) object); + return; } - } - - private void writeNumber(String name, Number object) throws IOException { - if (object instanceof Long) { - writeLong(name, object); - - } else if (object instanceof Integer) { - writeInteger(name, object); - - } else if (object instanceof Double) { - writeDouble(name, object); - - } else if (object instanceof BigDecimal) { - writeBigDecimal(name, object); - - } else if (object instanceof BigInteger) { - writeBigInteger(name, object); - - } else { - writeGeneralNumber(name, object); + jsonWriter.value((Long) object); + return; } + if (object instanceof Double) { + jsonWriter.value((Double) object); + return; + } + if (object instanceof Float) { + jsonWriter.value((Float) object); + return; + } + if (object instanceof BigDecimal) { + jsonWriter.value((BigDecimal) object); + return; + } + if (object instanceof Boolean) { + jsonWriter.value((Boolean) object); + return; + } + if (object instanceof Map) { + writeMap((Map) object, jsonWriter); + return; + } + if (object instanceof Collection) { + writeCollection((Collection) object, jsonWriter); + return; + } + + jsonWriter.value(object.toString()); } - private void writeGeneralNumber(String name, Number object) throws IOException { - writeBigDecimal(name, new BigDecimal(object.toString())); + /** + * Write map as Json content. + */ + private static void writeMap(Map map, io.avaje.json.JsonWriter jsonWriter) throws IOException { + jsonWriter.beginObject(); + for (Map.Entry entry : map.entrySet()) { + String fieldName = (String) entry.getKey(); + Object value = entry.getValue(); + jsonWriter.name(fieldName); + write(value, jsonWriter); + } + jsonWriter.endObject(); } - private void writeBigDecimal(String name, Number object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber((BigDecimal) object); - } else { - jsonGenerator.writeNumberField(name, (BigDecimal) object); + /** + * Write list as Json content. + */ + static void writeCollection(Collection list, io.avaje.json.JsonWriter jsonWriter) throws IOException { + jsonWriter.beginArray(); + for (Object element : list) { + write(element, jsonWriter); } + jsonWriter.endArray(); } - private void writeBigInteger(String name, Number object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber((BigInteger) object); - } else { - jsonGenerator.writeNumberField(name, object.longValue()); - } + /** + * Convert map to Json content. + */ + static String write(Map map) throws IOException { + StringWriter writer = new StringWriter(); + write(map, writer); + return writer.toString(); } - private void writeDouble(String name, Number object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber((Double) object); - } else { - jsonGenerator.writeNumberField(name, (Double) object); - } + /** + * Convert map to Json content. + */ + static io.avaje.json.JsonWriter write(Map map, Writer writer) throws IOException { + io.avaje.json.JsonWriter jsonWriter = JSON_STREAM.writer(writer); + jsonWriter.serializeNulls(true); + write(map, jsonWriter); + jsonWriter.flush(); + return jsonWriter; } - private void writeLong(String name, Number object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber((Long) object); - } else { - jsonGenerator.writeNumberField(name, (Long) object); + /** + * Convert map to Json content. + */ + static void write(Map map, io.avaje.json.JsonWriter jsonWriter) throws IOException { + jsonWriter.beginObject(); + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + jsonWriter.name(entry.getKey()); + write(entry.getValue(), jsonWriter); } - } - - private void writeInteger(String name, Number object) throws IOException { - if (name == null) { - jsonGenerator.writeNumber((Integer) object); - } else { - jsonGenerator.writeNumberField(name, (Integer) object); - } - } - - private void writeNull(String name) throws IOException { - if (name == null) { - jsonGenerator.writeNull(); - } else { - jsonGenerator.writeNullField(name); - } - } - - private void writeString(String name, String object) throws IOException { - if (name == null) { - jsonGenerator.writeString(object); - } else { - jsonGenerator.writeStringField(name, object); - } - } - - private void writeCollection(String name, Collection collection) throws IOException { - if (name != null) { - jsonGenerator.writeFieldName(name); - } - jsonGenerator.writeStartArray(); - for (Object object : collection) { - writeJson(null, object); - } - jsonGenerator.writeEndArray(); - } - - private void writeMap(String name, Map map) throws IOException { - - if (name != null) { - jsonGenerator.writeFieldName(name); - } - jsonGenerator.writeStartObject(); - Set> entrySet = map.entrySet(); - for (Entry entry : entrySet) { - writeJson(entry.getKey().toString(), entry.getValue()); - } - jsonGenerator.writeEndObject(); + jsonWriter.endObject(); } } diff --git a/ebean-core-json/src/main/java/module-info.java b/ebean-core-json/src/main/java/module-info.java index b7561ed62..0845699bd 100644 --- a/ebean-core-json/src/main/java/module-info.java +++ b/ebean-core-json/src/main/java/module-info.java @@ -1,8 +1,7 @@ module io.ebean.core.json { requires io.ebean.api; - - requires transitive com.fasterxml.jackson.core; + requires transitive io.avaje.json; exports io.ebeaninternal.json to io.ebean.test, io.ebean.core; provides io.ebean.service.BootstrapService with io.ebeaninternal.json.DJsonService; diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 2adbb33df..0cebee48b 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -20,10 +20,9 @@ - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - true + io.avaje + avaje-json-core + ${avaje-json-core.version} diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 9047befb0..31f49b7ad 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -1,7 +1,7 @@ package io.ebean.core.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.text.StringFormatter; import io.ebean.text.StringParser; @@ -177,13 +177,44 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData void writeData(DataOutput dataOutput, T value) throws IOException; /** - * Read the value from JsonParser. + * Read the value from JsonReader. */ - T jsonRead(JsonParser parser) throws IOException; + default T jsonRead(JsonReader parser) throws IOException { + JsonReader.Token token = parser.currentToken(); + if (token == JsonReader.Token.NULL) { + parser.isNullValue(); + return null; + } + if (token == JsonReader.Token.STRING) { + return parse(parser.readString()); + } + return parse(parser.readRaw()); + } /** - * Write the value to the JsonGenerator. + * Write the value to the JsonWriter. */ - void jsonWrite(JsonGenerator writer, T value) throws IOException; + default void jsonWrite(JsonWriter writer, T value) throws IOException { + if (value == null) { + writer.nullValue(); + return; + } + String formatted = formatValue(value); + if (formatted == null) { + writer.nullValue(); + return; + } + DocPropertyType docType = docType(); + if (docType == DocPropertyType.OBJECT || docType == DocPropertyType.LIST || docType == DocPropertyType.ROOT || likelyRawJson(formatted)) { + writer.rawValue(formatted); + } else { + writer.value(formatted); + } + } + + private static boolean likelyRawJson(String formatted) { + String trimmed = formatted.trim(); + return !trimmed.isEmpty() && (trimmed.charAt(0) == '{' || trimmed.charAt(0) == '['); + } } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDate.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDate.java index 5d76cfade..66550a4ed 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDate.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDate.java @@ -1,8 +1,8 @@ package io.ebean.core.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; import io.ebean.config.JsonConfig; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; @@ -83,20 +83,31 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { } @Override - public T jsonRead(JsonParser parser) throws IOException { - if (JsonToken.VALUE_NUMBER_INT == parser.getCurrentToken()) { - return convertFromMillis(parser.getLongValue()); - } else { - return convertFromDate(Date.valueOf(parser.getText())); + public T jsonRead(JsonReader parser) throws IOException { + Token token = parser.currentToken(); + if (Token.NUMBER == token) { + return convertFromMillis(parser.readLong()); } + if (Token.STRING == token) { + return convertFromDate(Date.valueOf(parser.readString())); + } + + String raw = parser.readRaw(); + if (raw == null || "null".equals(raw)) { + return null; + } + if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') { + return convertFromDate(Date.valueOf(raw.substring(1, raw.length() - 1))); + } + return convertFromMillis(Long.parseLong(raw)); } @Override - public void jsonWrite(JsonGenerator writer, T value) throws IOException { + public void jsonWrite(JsonWriter writer, T value) throws IOException { if (mode == JsonConfig.Date.ISO8601) { - writer.writeString(toIsoFormat(value)); + writer.value(toIsoFormat(value)); } else { - writer.writeNumber(convertToMillis(value)); + writer.value(convertToMillis(value)); } } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDateTime.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDateTime.java index 0ee2cb82f..95e7a9a89 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDateTime.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseDateTime.java @@ -1,7 +1,8 @@ package io.ebean.core.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; import io.ebean.config.JsonConfig; import java.io.DataInput; @@ -99,35 +100,53 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { } @Override - public T jsonRead(JsonParser parser) throws IOException { - switch (parser.getCurrentToken()) { - case VALUE_NUMBER_INT: { - return convertFromMillis(parser.getLongValue()); - } - case VALUE_NUMBER_FLOAT: { - BigDecimal value = parser.getDecimalValue(); - Timestamp timestamp = ScalarTypeUtils.toTimestamp(value); - return convertFromTimestamp(timestamp); - } - default: { - return fromJsonISO8601(parser.getText()); - } + public T jsonRead(JsonReader parser) throws IOException { + Token token = parser.currentToken(); + if (token == Token.NUMBER) { + return readNumber(parser.readDecimal()); } + if (token == Token.STRING) { + return fromStringValue(parser.readString()); + } + + String raw = parser.readRaw(); + if (raw == null || "null".equals(raw)) { + return null; + } + if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') { + return fromStringValue(raw.substring(1, raw.length() - 1)); + } + return readNumber(new BigDecimal(raw)); + } + + private T fromStringValue(String value) { + if (value.indexOf('-') == -1 && Character.isDigit(value.charAt(0))) { + return readNumber(new BigDecimal(value)); + } + return fromJsonISO8601(value); + } + + private T readNumber(BigDecimal value) { + if (value.scale() <= 0) { + return convertFromMillis(value.longValue()); + } + Timestamp timestamp = ScalarTypeUtils.toTimestamp(value); + return convertFromTimestamp(timestamp); } @Override - public void jsonWrite(JsonGenerator writer, T value) throws IOException { + public void jsonWrite(JsonWriter writer, T value) throws IOException { switch (mode) { case ISO8601: { - writer.writeString(toJsonISO8601(value)); + writer.value(toJsonISO8601(value)); break; } case NANOS: { - writer.writeNumber(toJsonNanos(value)); + writer.value(toJsonNanos(value)); break; } default: { - writer.writeNumber(convertToMillis(value)); + writer.value(convertToMillis(value)); } } } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseVarchar.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseVarchar.java index 99c83561b..f0646ee13 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseVarchar.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarTypeBaseVarchar.java @@ -1,7 +1,7 @@ package io.ebean.core.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import java.io.DataInput; import java.io.DataOutput; @@ -104,13 +104,16 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { } @Override - public T jsonRead(JsonParser parser) throws IOException { - return parse(parser.getValueAsString()); + public T jsonRead(JsonReader parser) throws IOException { + if (parser.isNullValue()) { + return null; + } + return parse(parser.readString()); } @Override - public void jsonWrite(JsonGenerator writer, T value) throws IOException { - writer.writeString(format(value)); + public void jsonWrite(JsonWriter writer, T value) throws IOException { + writer.value(format(value)); } @Override diff --git a/ebean-core-type/src/main/java/module-info.java b/ebean-core-type/src/main/java/module-info.java index 88be6d462..9c48ef295 100644 --- a/ebean-core-type/src/main/java/module-info.java +++ b/ebean-core-type/src/main/java/module-info.java @@ -4,8 +4,7 @@ module io.ebean.core.type { requires transitive java.sql; requires transitive io.ebean.api; + requires transitive io.avaje.json; requires static org.postgresql.jdbc; - requires static com.fasterxml.jackson.core; - } diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index d993d56a8..33094da19 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -138,14 +138,6 @@ - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - true - - com.fasterxml.jackson.core jackson-databind diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiJsonContext.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiJsonContext.java index 2700ed60e..a868c17b9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiJsonContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiJsonContext.java @@ -1,6 +1,6 @@ package io.ebeaninternal.api; -import com.fasterxml.jackson.core.JsonGenerator; +import io.avaje.json.JsonWriter; import io.ebean.plugin.BeanType; import io.ebean.text.json.JsonContext; import io.ebean.text.json.JsonWriteOptions; @@ -17,7 +17,7 @@ public interface SpiJsonContext extends JsonContext { /** * Create a Json Writer for writing beans as JSON. */ - SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options); + SpiJsonWriter createJsonWriter(JsonWriter gen, JsonWriteOptions options); /** * Create a Json Writer for writing beans as JSON supplying a writer. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java b/ebean-core/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java index 613b105cc..125c933d9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java @@ -1,8 +1,8 @@ package io.ebeaninternal.api.json; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.bean.EntityBean; import io.ebean.bean.PersistenceContext; import io.ebeaninternal.server.deploy.BeanDescriptor; @@ -14,7 +14,9 @@ public interface SpiJsonReader { PersistenceContext persistenceContext(); - SpiJsonReader forJson(JsonParser moreJson); + SpiJsonReader forJson(JsonReader moreJson); + + SpiJsonReader forJson(String moreJson); void persistenceContextPut(Object beanId, T currentBean); @@ -22,9 +24,9 @@ public interface SpiJsonReader { ObjectMapper mapper(); - JsonParser parser(); + JsonReader parser(); - JsonToken nextToken() throws IOException; + Token nextToken() throws IOException; void pushPath(String path); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java index 5d71023b8..dae1a1138 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.changelog; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; +import io.avaje.json.JsonWriter; +import io.avaje.json.stream.JsonStream; import io.ebean.event.changelog.BeanChange; import io.ebean.event.changelog.ChangeSet; import io.ebean.event.changelog.ChangeType; @@ -15,13 +15,13 @@ import java.util.Map; */ final class ChangeJsonBuilder { - private final JsonFactory jsonFactory = new JsonFactory(); + private final JsonStream jsonStream = JsonStream.builder().build(); /** * Write the bean change as JSON. */ void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException { - try (JsonGenerator generator = jsonFactory.createGenerator(writer)) { + try (JsonWriter generator = jsonStream.writer(writer)) { writeBeanChange(generator, bean, changeSet); generator.flush(); } @@ -30,58 +30,67 @@ final class ChangeJsonBuilder { /** * Write the bean change as JSON document containing the transaction header details. */ - private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException { - gen.writeStartObject(); - gen.writeNumberField("ts", bean.getEventTime()); - gen.writeStringField("change", bean.getEvent().getCode()); - gen.writeStringField("type", bean.getType()); - gen.writeStringField("id", bean.getId().toString()); + private void writeBeanChange(JsonWriter gen, BeanChange bean, ChangeSet changeSet) { + gen.beginObject(); + gen.name("ts"); + gen.value(bean.getEventTime()); + gen.name("change"); + gen.value(bean.getEvent().getCode()); + gen.name("type"); + gen.value(bean.getType()); + gen.name("id"); + gen.value(bean.getId().toString()); if (bean.getTenantId() != null) { - gen.writeStringField("tenantId", bean.getTenantId().toString()); + gen.name("tenantId"); + gen.value(bean.getTenantId().toString()); } writeBeanTransactionDetails(gen, changeSet); writeBeanValues(gen, bean); - gen.writeEndObject(); + gen.endObject(); } /** * Denormalise by writing the transaction header details. */ - private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException { + private void writeBeanTransactionDetails(JsonWriter gen, ChangeSet changeSet) { String source = changeSet.getSource(); if (source != null) { - gen.writeStringField("source", source); + gen.name("source"); + gen.value(source); } String userId = changeSet.getUserId(); if (userId != null) { - gen.writeStringField("userId", userId); + gen.name("userId"); + gen.value(userId); } String userIpAddress = changeSet.getUserIpAddress(); if (userIpAddress != null) { - gen.writeStringField("userIpAddress", userIpAddress); + gen.name("userIpAddress"); + gen.value(userIpAddress); } Map userContext = changeSet.getUserContext(); if (userContext != null && !userContext.isEmpty()) { - gen.writeObjectFieldStart("userContext"); + gen.name("userContext"); + gen.beginObject(); for (Map.Entry entry : userContext.entrySet()) { - gen.writeStringField(entry.getKey(), entry.getValue()); + gen.name(entry.getKey()); + gen.value(entry.getValue()); } - gen.writeEndObject(); + gen.endObject(); } } /** * For insert and update write the new/old values. */ - private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException { + private void writeBeanValues(JsonWriter gen, BeanChange bean) { if (bean.getEvent() != ChangeType.DELETE) { - gen.writeFieldName("data"); - gen.writeRaw(":"); - gen.writeRaw(bean.getData()); + gen.name("data"); + gen.rawValue(bean.getData()); String oldData = bean.getOldData(); if (oldData != null) { - gen.writeRaw(",\"oldData\":"); - gen.writeRaw(oldData); + gen.name("oldData"); + gen.rawValue(oldData); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java index e90716ccd..158080b28 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.core; -import com.fasterxml.jackson.core.JsonFactory; +import io.avaje.json.stream.JsonStream; import io.ebean.DatabaseBuilder; import io.ebean.ExpressionFactory; import io.ebean.annotation.Platform; @@ -89,7 +89,7 @@ public final class InternalConfiguration { private final boolean jacksonCorePresent; private final ExpressionFactory expressionFactory; private final SpiBackgroundExecutor backgroundExecutor; - private final JsonFactory jsonFactory; + private final JsonStream jsonStream; private final DocStoreFactory docStoreFactory; private final List plugins = new ArrayList<>(); private final MultiValueBind multiValueBind; @@ -108,7 +108,7 @@ public final class InternalConfiguration { this.tableModState = new TableModState(); this.logManager = initLogManager(); this.docStoreFactory = initDocStoreFactory(service(DocStoreFactory.class)); - this.jsonFactory = config.getJsonFactory(); + this.jsonStream = config.getJsonStream(); this.clusterManager = clusterManager; this.backgroundExecutor = backgroundExecutor; this.bootupClasses = bootupClasses; @@ -292,7 +292,7 @@ public final class InternalConfiguration { } SpiJsonContext createJsonContext(SpiEbeanServer server) { - return jacksonCorePresent ? new DJsonContext(server, jsonFactory, typeManager) : null; + return jacksonCorePresent ? new DJsonContext(server, jsonStream, typeManager) : null; } AutoTuneService createAutoTuneService(SpiEbeanServer server) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java index 66b3faa45..9fc447c76 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java @@ -2,8 +2,11 @@ package io.ebeaninternal.server.deploy; import io.ebean.PersistenceIOException; import io.ebean.bean.BeanDiffVisitor; +import io.ebean.config.JsonConfig; import io.ebeaninternal.api.json.SpiJsonWriter; +import io.ebeaninternal.server.json.WriteJson; import io.ebeaninternal.server.util.ArrayStack; +import io.avaje.json.stream.JsonStream; import java.io.IOException; import java.io.StringWriter; @@ -13,26 +16,18 @@ import java.io.StringWriter; */ final class BeanChangeJson implements BeanDiffVisitor { - private final StringWriter newData; - private final StringWriter oldData; - private final SpiJsonWriter newJson; - private final SpiJsonWriter oldJson; + private final StringWriter data; + private final SpiJsonWriter json; + private final boolean writeNew; private final ArrayStack> stack = new ArrayStack<>(); private BeanDescriptor descriptor; - BeanChangeJson(BeanDescriptor descriptor, boolean statelessUpdate) { + BeanChangeJson(BeanDescriptor descriptor, boolean writeNew) { this.descriptor = descriptor; - this.newData = new StringWriter(200); - this.newJson = descriptor.createJsonWriter(newData); - newJson.writeStartObject(); - if (statelessUpdate) { - this.oldJson = null; - this.oldData = null; - } else { - this.oldData = new StringWriter(200); - this.oldJson = descriptor.createJsonWriter(oldData); - oldJson.writeStartObject(); - } + this.writeNew = writeNew; + this.data = new StringWriter(200); + this.json = new WriteJson(JsonStream.builder().build().writer(data), JsonConfig.Include.ALL); + json.writeStartObject(); } @Override @@ -40,10 +35,7 @@ final class BeanChangeJson implements BeanDiffVisitor { try { BeanProperty prop = descriptor.propertiesIndex[position]; if (prop.isDbUpdatable()) { - prop.jsonWriteValue(newJson, newVal); - if (oldJson != null) { - prop.jsonWriteValue(oldJson, oldVal); - } + prop.jsonWriteValue(json, writeNew ? newVal : oldVal); } } catch (IOException e) { throw new PersistenceIOException(e); @@ -55,18 +47,12 @@ final class BeanChangeJson implements BeanDiffVisitor { stack.push(descriptor); BeanPropertyAssocOne embedded = (BeanPropertyAssocOne)descriptor.propertiesIndex[position]; descriptor = embedded.targetDescriptor(); - newJson.writeStartObject(embedded.name()); - if (oldJson != null) { - oldJson.writeStartObject(embedded.name()); - } + json.writeStartObject(embedded.name()); } @Override public void visitPop() { - newJson.writeEndObject(); - if (oldJson != null) { - oldJson.writeEndObject(); - } + json.writeEndObject(); descriptor = stack.pop(); } @@ -75,28 +61,14 @@ final class BeanChangeJson implements BeanDiffVisitor { */ void flush() { try { - newJson.writeEndObject(); - newJson.flush(); - if (oldJson != null) { - oldJson.writeEndObject(); - oldJson.flush(); - } + json.writeEndObject(); + json.flush(); } catch (IOException e) { throw new PersistenceIOException(e); } } - /** - * Return the new values JSON. - */ - String newJson() { - return newData.toString(); - } - - /** - * Return the old values JSON. - */ - String oldJson() { - return oldData == null ? null : oldData.toString(); + String json() { + return data.toString(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 5dfdd705b..16da6b307 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -792,10 +792,17 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { */ private BeanChange updateBeanChange(PersistRequestBean request) { try { - BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate()); - request.intercept().addDirtyPropertyValues(changeJson); - changeJson.flush(); - return beanChange(ChangeType.UPDATE, request.beanId(), changeJson.newJson(), changeJson.oldJson()); + BeanChangeJson newValues = new BeanChangeJson(this, true); + request.intercept().addDirtyPropertyValues(newValues); + newValues.flush(); + String oldData = null; + if (!request.isStatelessUpdate()) { + BeanChangeJson oldValues = new BeanChangeJson(this, false); + request.intercept().addDirtyPropertyValues(oldValues); + oldValues.flush(); + oldData = oldValues.json(); + } + return beanChange(ChangeType.UPDATE, request.beanId(), newValues.json(), oldData); } catch (RuntimeException e) { log.log(ERROR, "Failed to write ChangeLog entry for update", e); return null; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java index ecb283e5c..04dbaec5e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.bean.EntityBean; import io.ebean.core.type.ScalarType; import io.ebeaninternal.api.json.SpiJsonReader; @@ -49,25 +49,25 @@ class BeanDescriptorElementEmbeddedMap extends BeanDescriptorElementEmbedded< @Override public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { - JsonParser parser = readJson.parser(); + JsonReader parser = readJson.parser(); ElementCollector add = elementHelp.createCollector(); - do { - String fieldName = parser.nextFieldName(); - if (fieldName == null) { - break; - } + parser.beginObject(); + while (parser.hasNextField()) { + String fieldName = parser.nextField(); if (stringKey) { - parser.nextToken(); Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here? add.addKeyValue(fieldName, val); } else { - parser.nextFieldName(); + parser.beginObject(); + parser.nextField(); Object key = scalarTypeKey.jsonRead(parser); - parser.nextFieldName(); + parser.nextField(); Object val = readJsonElement(readJson, null, null); // CHECKME: Update existing map entry here? + parser.endObject(); add.addKeyValue(key, val); } - } while (true); + } + parser.endObject(); return add.collection(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java index 0242c993f..d50b9c234 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java @@ -1,7 +1,6 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; import io.ebean.PersistenceIOException; import io.ebean.SqlUpdate; import io.ebean.bean.EntityBean; @@ -40,15 +39,13 @@ class BeanDescriptorElementScalar extends BeanDescriptorElement { @Override public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { - JsonParser parser = readJson.parser(); + JsonReader parser = readJson.parser(); ElementCollector add = elementHelp.createCollector(); - do { - JsonToken token = parser.nextToken(); - if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) { - break; - } + parser.beginArray(); + while (parser.hasNextElement()) { add.addElement(scalarType.jsonRead(parser)); - } while (true); + } + parser.endArray(); return add.collection(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java index d5b8d91a9..4de87ebdb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.bean.EntityBean; import io.ebean.core.type.ScalarType; import io.ebeaninternal.api.json.SpiJsonReader; @@ -49,25 +49,25 @@ class BeanDescriptorElementScalarMap extends BeanDescriptorElement { @Override public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { - JsonParser parser = readJson.parser(); + JsonReader parser = readJson.parser(); ElementCollector add = elementHelp.createCollector(); - do { - String fieldName = parser.nextFieldName(); - if (fieldName == null) { - break; - } + parser.beginObject(); + while (parser.hasNextField()) { + String fieldName = parser.nextField(); if (stringKey) { - parser.nextToken(); Object val = scalarTypeVal.jsonRead(parser); add.addKeyValue(fieldName, val); } else { - parser.nextFieldName(); + parser.beginObject(); + parser.nextField(); Object key = scalarTypeKey.jsonRead(parser); - parser.nextFieldName(); + parser.nextField(); Object val = scalarTypeVal.jsonRead(parser); + parser.endObject(); add.addKeyValue(key, val); } - } while (true); + } + parser.endObject(); return add.collection(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java index 720ef92ca..46587cc89 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -1,10 +1,7 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.bean.EntityBean; import io.ebean.text.json.EJson; import io.ebeaninternal.api.json.SpiJsonReader; @@ -34,7 +31,8 @@ final class BeanDescriptorJsonHelp { InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); String discValue = localInheritInfo.getDiscriminatorStringValue(); String discColumn = localInheritInfo.getDiscriminatorColumn(); - writeJson.gen().writeStringField(discColumn, discValue); + writeJson.gen().name(discColumn); + writeJson.gen().value(discValue); localInheritInfo.desc().jsonWriteProperties(writeJson, bean); } writeJson.writeEndObject(); @@ -66,44 +64,38 @@ final class BeanDescriptorJsonHelp { @SuppressWarnings("unchecked") T jsonRead(SpiJsonReader jsonRead, String path, boolean withInheritance, T target) throws IOException { - JsonParser parser = jsonRead.parser(); - //noinspection StatementWithEmptyBody - if (parser.getCurrentToken() == JsonToken.START_OBJECT) { - // start object token read by Jackson already - } else { - // check for null or start object - JsonToken token = parser.nextToken(); - if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) { - return null; - } - if (JsonToken.START_OBJECT != token) { - throw new JsonParseException(parser, "Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation()); - } + JsonReader parser = jsonRead.parser(); + if (parser.isNullValue()) { + return null; + } + Token token = parser.currentToken(); + if (token != Token.BEGIN_OBJECT) { + throw new IllegalStateException("Unexpected token " + token + " - expecting BEGIN_OBJECT at: " + parser.location()); } if (desc.inheritInfo == null || !withInheritance) { return jsonReadObject(jsonRead, path, target); } - ObjectNode node = jsonRead.mapper().readTree(parser); - if (node.isNull()) { + Map node = EJson.parseObject(parser); + if (node == null) { return null; } - JsonParser newParser = node.traverse(); - SpiJsonReader newReader = jsonRead.forJson(newParser); // check for the discriminator value to determine the correct sub type String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); - JsonNode discNode = node.get(discColumn); - if (discNode == null || discNode.isNull()) { + Object discValue = node.get(discColumn); + String rawObject = EJson.write(node); + SpiJsonReader newReader = jsonRead.forJson(rawObject); + if (discValue == null) { if (!desc.isAbstractType()) { return desc.jsonReadObject(newReader, path, target); } String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; - throw new JsonParseException(newParser, msg, parser.getCurrentLocation()); + throw new IllegalStateException(msg); } - BeanDescriptor inheritDesc = (BeanDescriptor) inheritInfo.readType(discNode.asText()).desc(); + BeanDescriptor inheritDesc = (BeanDescriptor) inheritInfo.readType(String.valueOf(discValue)).desc(); return inheritDesc.jsonReadObject(newReader, path, target); } @@ -124,35 +116,30 @@ final class BeanDescriptorJsonHelp { if (path != null) { readJson.pushPath(path); } + JsonReader parser = readJson.parser(); + parser.beginObject(); + // unmapped properties, send to JsonReadBeanVisitor later Map unmappedProperties = null; - do { - JsonParser parser = readJson.parser(); - JsonToken event = parser.nextToken(); - if (JsonToken.FIELD_NAME == event) { - String key = parser.getCurrentName(); - BeanProperty p = desc.beanProperty(key); - if (p != null) { - if (p.isVersion() && readJson.update() ) { - // skip version prop during update - p.jsonRead(readJson); - } else { - p.jsonRead(readJson, bean); - } + while (parser.hasNextField()) { + String key = parser.nextField(); + BeanProperty p = desc.beanProperty(key); + if (p != null) { + if (p.isVersion() && readJson.update()) { + // skip version prop during update + p.jsonRead(readJson); } else { - // read an unmapped property - if (unmappedProperties == null) { - unmappedProperties = new LinkedHashMap<>(); - } - unmappedProperties.put(key, EJson.parse(parser)); + p.jsonRead(readJson, bean); } - } else if (JsonToken.END_OBJECT == event) { - break; } else { - throw new RuntimeException("Unexpected token " + event + " - expecting key or end_object at: " + parser.getCurrentLocation()); + // read an unmapped property + if (unmappedProperties == null) { + unmappedProperties = new LinkedHashMap<>(); + } + unmappedProperties.put(key, EJson.parse(parser)); } - - } while (true); + } + parser.endObject(); if (unmappedProperties != null) { desc.setUnmappedJson(bean, unmappedProperties); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 2c6443b77..9e9579637 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.DataIntegrityException; import io.ebean.ModifyAwareType; import io.ebean.ValuePair; @@ -1437,14 +1438,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } public Object jsonRead(SpiJsonReader ctx) throws IOException { - JsonToken event = ctx.nextToken(); - if (JsonToken.VALUE_NULL == event) { + JsonReader parser = ctx.parser(); + if (parser.isNullValue()) { return null; } else { // expect to read non-null json value Object objValue; if (scalarType != null) { - objValue = scalarType.jsonRead(ctx.parser()); + objValue = scalarType.jsonRead(parser); } else { try { objValue = ctx.readValueUsingObjectMapper(propertyType); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 2374b24ec..6c82b9dc3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1,15 +1,14 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollection.ModifyListenMode; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; import io.ebean.plugin.PropertyAssocMany; import io.ebean.text.PathProperties; @@ -1043,9 +1042,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST */ private Object jsonReadCollection(String json) throws IOException { SpiJsonReader ctx = descriptor.createJsonReader(json); - JsonParser parser = ctx.parser(); - JsonToken event = parser.nextToken(); - if (JsonToken.VALUE_NULL == event) { + JsonReader parser = ctx.parser(); + if (parser.isNullValue()) { return null; } return jsonReadCollection(ctx, null, null); @@ -1068,20 +1066,18 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST BeanCollection collection = createEmpty(parentBean); BeanCollectionAdd add = beanCollectionAdd(collection); Map existingBeans = extractBeans(targets); - do { - + JsonReader parser = readJson.parser(); + parser.beginArray(); + while (parser.hasNextElement()) { EntityBean detailBean = getDetailBean(readJson, existingBeans); - if (detailBean == null) { - // read the entire array - break; + if (detailBean != null) { + add.addEntityBean(detailBean); + if (parentBean != null && childMasterProperty != null) { + // bind detail bean back to master via mappedBy property + childMasterProperty.setValue(detailBean, parentBean); + } } - add.addEntityBean(detailBean); - - if (parentBean != null && childMasterProperty != null) { - // bind detail bean back to master via mappedBy property - childMasterProperty.setValue(detailBean, parentBean); - } - } while (true); + } return collection; } @@ -1092,18 +1088,79 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST BeanProperty idProperty = targetDescriptor.idProperty(); if (targets == null || idProperty == null) { return (EntityBean) targetDescriptor.jsonRead(readJson, name, null); - } else { - JsonToken token = readJson.parser().nextToken(); - if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) { - return null; - } - // extract the id. We have to buffer the JSON; - ObjectNode node = readJson.mapper().readTree(readJson.parser()); - SpiJsonReader jsonReader = readJson.forJson(node.traverse()); - JsonNode idNode = node.get(idProperty.name()); - Object id = idNode == null ? null : idProperty.jsonRead(readJson.forJson(idNode.traverse())); - return (EntityBean) targetDescriptor.jsonRead(jsonReader, name, targets.get(id)); } + /// ROB Checkme + JsonReader parser = readJson.parser(); + if (parser.isNullValue()) { + return null; + } + EntityBean incomingBean = (EntityBean) targetDescriptor.jsonRead(readJson, name, null); + Object id = idProperty.getValue(incomingBean); + EntityBean existingBean = (EntityBean) targets.get(id); + if (existingBean == null) { + return incomingBean; + } + mergeLoadedBean(targetDescriptor, incomingBean, existingBean); + return existingBean; + } + + private void mergeLoadedBean(BeanDescriptor descriptor, EntityBean incomingBean, EntityBean existingBean) { + EntityBeanIntercept incomingIntercept = incomingBean._ebean_getIntercept(); + for (BeanProperty property : descriptor.propertiesBaseScalar()) { + if (incomingIntercept.isLoadedProperty(property.propertyIndex())) { + property.setValueIntercept(existingBean, property.getValue(incomingBean)); + } + } + for (BeanPropertyAssocMany property : descriptor.propertiesMany()) { + if (incomingIntercept.isLoadedProperty(property.propertyIndex())) { + Object mergedValue = mergeLoadedMany(property, property.getValue(incomingBean), property.getValue(existingBean)); + property.setValueIntercept(existingBean, mergedValue); + } + } + } + + private Object mergeLoadedMany(BeanPropertyAssocMany property, Object incomingValue, Object existingValue) { + if (!(incomingValue instanceof Collection)) { + return incomingValue; + } + if (!(existingValue instanceof Collection)) { + return incomingValue; + } + Collection incomingCollection = (Collection) incomingValue; + Collection existingCollection = (Collection) existingValue; + BeanProperty idProperty = property.targetDescriptor.idProperty(); + if (idProperty == null) { + return incomingValue; + } + + Map existingById = new LinkedHashMap<>(); + for (Object existingElement : existingCollection) { + if (existingElement instanceof EntityBean) { + EntityBean existingBean = (EntityBean) existingElement; + Object id = idProperty.getValue(existingBean); + if (id != null) { + existingById.put(id, existingBean); + } + } + } + + BeanCollection mergedCollection = property.createEmpty(null); + BeanCollectionAdd add = property.beanCollectionAdd(mergedCollection); + for (Object incomingElement : incomingCollection) { + if (!(incomingElement instanceof EntityBean)) { + continue; + } + EntityBean incomingBean = (EntityBean) incomingElement; + Object id = idProperty.getValue(incomingBean); + EntityBean existingBean = id == null ? null : existingById.get(id); + if (existingBean == null) { + add.addEntityBean(incomingBean); + } else { + mergeLoadedBean(property.targetDescriptor, incomingBean, existingBean); + add.addEntityBean(existingBean); + } + } + return mergedCollection; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java index 4bc7307fe..d22b18f8c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -1,8 +1,7 @@ package io.ebeaninternal.server.deploy; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; import io.ebean.bean.EntityBean; import io.ebeaninternal.api.json.SpiJsonReader; @@ -38,17 +37,17 @@ class BeanPropertyAssocManyJsonHelp { if (!this.many.jsonDeserialize) { return; } - JsonParser parser = readJson.parser(); - JsonToken event = parser.nextToken(); - if (JsonToken.VALUE_NULL == event) { + JsonReader parser = readJson.parser(); + if (parser.isNullValue()) { return; } if (many.isTransient()) { jsonReadTransientUsingObjectMapper(readJson, parentBean); return; } - if (JsonToken.START_ARRAY != event && JsonToken.START_OBJECT != event) { - throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start array or object"); + Token event = parser.currentToken(); + if (Token.BEGIN_ARRAY != event && Token.BEGIN_OBJECT != event) { + throw new IllegalStateException("Unexpected token " + event + " - expecting start array or object"); } if (readJson.update()) { many.setValueIntercept(parentBean, many.jsonReadCollection(readJson, parentBean, many.getValue(parentBean))); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java index ae82bd7c6..92d58688c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java @@ -29,11 +29,11 @@ class BeanPropertyAssocManyJsonTransient { TypeFactory typeFactory = mapper.getTypeFactory(); JavaType target = typeFactory.constructType(many.targetType()); MapType jacksonType = typeFactory.constructMapType(LinkedHashMap.class, TypeFactory.unknownType(), target); - value = mapper.readValue(readJson.parser(), jacksonType); + value = mapper.readValue(readJson.parser().readRaw(), jacksonType); } else { // read list or set using Jackson object mapper CollectionType jacksonType = mapper.getTypeFactory().constructCollectionType(manyType.getCollectionType(), many.targetType()); - value = mapper.readValue(readJson.parser(), jacksonType); + value = mapper.readValue(readJson.parser().readRaw(), jacksonType); } many.setValue(parentBean, value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonBeanReader.java b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonBeanReader.java index f1b92a78b..08add2b91 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonBeanReader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonBeanReader.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.json; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.PersistenceIOException; import io.ebean.bean.PersistenceContext; import io.ebean.text.json.JsonBeanReader; @@ -45,7 +45,7 @@ public final class DJsonBeanReader implements JsonBeanReader { } @Override - public JsonBeanReader forJson(JsonParser moreJson) { + public JsonBeanReader forJson(JsonReader moreJson) { return new DJsonBeanReader<>(desc, readJson.forJson(moreJson)); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonContext.java index 39b80344b..6e9017540 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonContext.java @@ -1,12 +1,9 @@ package io.ebeaninternal.server.json; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.PrettyPrinter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; +import io.avaje.json.stream.JsonStream; import io.ebean.FetchPath; import io.ebean.bean.EntityBean; import io.ebean.config.JsonConfig; @@ -28,7 +25,6 @@ import io.ebeaninternal.util.ParamTypeHelper.TypeInfo; import java.io.IOException; import java.io.Reader; -import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; @@ -45,30 +41,22 @@ import java.util.Set; */ public final class DJsonContext implements SpiJsonContext { - private static final PrettyPrinter PRETTY_PRINTER = new Pretty(); - private final SpiEbeanServer server; - private final JsonFactory jsonFactory; + private final JsonStream jsonStream; private final Object defaultObjectMapper; private final JsonConfig.Include defaultInclude; private final DJsonScalar jsonScalar; - private static class Pretty extends DefaultPrettyPrinter { - Pretty() { - _objectFieldValueSeparatorWithSpaces = ": "; - } - } - - public DJsonContext(SpiEbeanServer server, JsonFactory jsonFactory, TypeManager typeManager) { + public DJsonContext(SpiEbeanServer server, JsonStream jsonStream, TypeManager typeManager) { this.server = server; - this.jsonFactory = (jsonFactory != null) ? jsonFactory : new JsonFactory(); + this.jsonStream = jsonStream; this.defaultObjectMapper = this.server.config().getObjectMapper(); this.defaultInclude = this.server.config().getJsonInclude(); this.jsonScalar = new DJsonScalar(typeManager); } @Override - public void writeScalar(JsonGenerator generator, Object scalarValue) throws IOException { + public void writeScalar(JsonWriter generator, Object scalarValue) throws IOException { jsonScalar.write(generator, scalarValue); } @@ -78,31 +66,34 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public JsonGenerator createGenerator(Writer writer) throws JsonIOException { + public JsonWriter createGenerator(Writer writer) throws JsonIOException { + JsonWriter jsonWriter = stream().writer(writer); + jsonWriter.serializeNulls(defaultInclude == JsonConfig.Include.ALL); + jsonWriter.serializeEmpty(defaultInclude != JsonConfig.Include.NON_EMPTY); + return jsonWriter; + } + + @Override + public JsonReader createParser(Reader reader) throws JsonIOException { try { - return jsonFactory.createGenerator(writer); + return createParser(readAll(reader)); } catch (IOException e) { throw new JsonIOException(e); } } - @Override - public JsonParser createParser(Reader reader) throws JsonIOException { - try { - return jsonFactory.createParser(reader); - } catch (IOException e) { - throw new JsonIOException(e); - } + private JsonStream stream() { + return (jsonStream != null) ? jsonStream : JsonStream.builder().build(); } @Override public T toBean(Class cls, String json) throws JsonIOException { - return toBean(cls, new StringReader(json)); + return toBean(cls, createParser(json)); } @Override public T toBean(Class cls, String json, JsonReadOptions options) throws JsonIOException { - return toBean(cls, new StringReader(json), options); + return toBean(cls, createParser(json), options); } @Override @@ -116,15 +107,15 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public T toBean(Class cls, JsonParser parser) throws JsonIOException { + public T toBean(Class cls, JsonReader parser) throws JsonIOException { return toBean(cls, parser, null); } @Override - public T toBean(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException { + public T toBean(Class cls, JsonReader parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); try { - return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), false), null, null); + return desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream()), null, null); } catch (IOException e) { throw new JsonIOException(e); } @@ -132,12 +123,12 @@ public final class DJsonContext implements SpiJsonContext { @Override public void toBean(T target, String json) throws JsonIOException { - toBean(target, new StringReader(json)); + toBean(target, createParser(json)); } @Override public void toBean(T target, String json, JsonReadOptions options) throws JsonIOException { - toBean(target, new StringReader(json), options); + toBean(target, createParser(json), options); } @Override @@ -151,42 +142,42 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public void toBean(T target, JsonParser parser) throws JsonIOException { + public void toBean(T target, JsonReader parser) throws JsonIOException { toBean(target, parser, null); } @SuppressWarnings("unchecked") @Override - public void toBean(T target, JsonParser parser, JsonReadOptions options) throws JsonIOException { + public void toBean(T target, JsonReader parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = (BeanDescriptor) getDescriptor(target.getClass()); try { - desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), target != null), null, target); + desc.jsonRead(new ReadJson(desc, parser, options, determineObjectMapper(options), target != null, stream()), null, target); } catch (IOException e) { throw new JsonIOException(e); } } @Override - public DJsonBeanReader createBeanReader(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException { + public DJsonBeanReader createBeanReader(Class cls, JsonReader parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); - return new DJsonBeanReader<>(desc, new ReadJson(desc, parser, options, determineObjectMapper(options), false)); + return new DJsonBeanReader<>(desc, new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream())); } @Override - public DJsonBeanReader createBeanReader(BeanType beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException { + public DJsonBeanReader createBeanReader(BeanType beanType, JsonReader parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = (BeanDescriptor) beanType; - SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options), false); + SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options), false, stream()); return new DJsonBeanReader<>(desc, readJson); } @Override public List toList(Class cls, String json) throws JsonIOException { - return toList(cls, new StringReader(json)); + return toList(cls, createParser(json)); } @Override public List toList(Class cls, String json, JsonReadOptions options) throws JsonIOException { - return toList(cls, new StringReader(json), options); + return toList(cls, createParser(json), options); } @Override @@ -200,35 +191,30 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public List toList(Class cls, JsonParser src) throws JsonIOException { + public List toList(Class cls, JsonReader src) throws JsonIOException { return toList(cls, src, null); } @Override - public List toList(Class cls, JsonParser src, JsonReadOptions options) throws JsonIOException { + public List toList(Class cls, JsonReader src, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); - SpiJsonReader readJson = new ReadJson(desc, src, options, determineObjectMapper(options), false); + SpiJsonReader readJson = new ReadJson(desc, src, options, determineObjectMapper(options), false, stream()); try { - - JsonToken currentToken = src.getCurrentToken(); - if (currentToken != JsonToken.START_ARRAY) { - JsonToken event = src.nextToken(); - if (event != JsonToken.START_ARRAY) { - throw new JsonParseException(src, "Expecting start_array event but got " + event); - } + if (src.isNullValue()) { + return null; + } + Token currentToken = src.currentToken(); + if (currentToken != Token.BEGIN_ARRAY) { + throw new JsonIOException("Expecting BEGIN_ARRAY but got " + currentToken); } - List list = new ArrayList<>(); - do { - // CHECKME: Should we update the list + src.beginArray(); + while (src.hasNextElement()) { T bean = desc.jsonRead(readJson, null, null); - if (bean == null) { - break; - } else { + if (bean != null) { list.add(bean); } - } while (true); - + } return list; } catch (IOException e) { throw new JsonIOException(e); @@ -237,7 +223,7 @@ public final class DJsonContext implements SpiJsonContext { @Override public Object toObject(Type genericType, String json) throws JsonIOException { - return toObject(genericType, createParser(new StringReader(json))); + return toObject(genericType, createParser(json)); } @Override @@ -245,8 +231,22 @@ public final class DJsonContext implements SpiJsonContext { return toObject(genericType, createParser(json)); } + private JsonReader createParser(String json) { + return stream().reader(json); + } + + private String readAll(Reader reader) throws IOException { + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[2048]; + int len; + while ((len = reader.read(buffer)) != -1) { + builder.append(buffer, 0, len); + } + return builder.toString(); + } + @Override - public Object toObject(Type genericType, JsonParser jsonParser) throws JsonIOException { + public Object toObject(Type genericType, JsonReader jsonParser) throws JsonIOException { TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); ManyType manyType = info.getManyType(); switch (manyType) { @@ -262,19 +262,19 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public void toJson(Object value, JsonGenerator generator) throws JsonIOException { + public void toJson(Object value, JsonWriter generator) throws JsonIOException { // generator passed in so don't close it toJsonNoClose(value, generator, null); } @Override - public void toJson(Object value, JsonGenerator generator, FetchPath fetchPath) throws JsonIOException { + public void toJson(Object value, JsonWriter generator, FetchPath fetchPath) throws JsonIOException { // generator passed in so don't close it toJsonNoClose(value, generator, JsonWriteOptions.pathProperties(fetchPath)); } @Override - public void toJson(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { + public void toJson(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException { // generator passed in so don't close it toJsonNoClose(o, generator, options); } @@ -303,9 +303,9 @@ public final class DJsonContext implements SpiJsonContext { } /** - * Write to the JsonGenerator and close when complete. + * Write to the JsonWriter and close when complete. */ - private void toJsonWithClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { + private void toJsonWithClose(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException { try { toJsonInternal(o, generator, options); generator.close(); @@ -315,9 +315,9 @@ public final class DJsonContext implements SpiJsonContext { } /** - * Write to the JsonGenerator and without closing it (as it was created externally). + * Write to the JsonWriter and without closing it (as it was created externally). */ - private void toJsonNoClose(Object o, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException { + private void toJsonNoClose(Object o, JsonWriter generator, JsonWriteOptions options) throws JsonIOException { try { toJsonInternal(o, generator, options); } catch (IOException e) { @@ -342,9 +342,9 @@ public final class DJsonContext implements SpiJsonContext { private String toJsonString(Object value, JsonWriteOptions options, boolean pretty) throws JsonIOException { StringWriter writer = new StringWriter(500); - try (JsonGenerator gen = createGenerator(writer)) { + try (JsonWriter gen = createGenerator(writer)) { if (pretty) { - gen.setPrettyPrinter(PRETTY_PRINTER); + gen.pretty(true); } toJsonInternal(value, gen, options); } catch (IOException e) { @@ -354,29 +354,23 @@ public final class DJsonContext implements SpiJsonContext { } @SuppressWarnings("unchecked") - private void toJsonInternal(Object value, JsonGenerator gen, JsonWriteOptions options) throws IOException { + private void toJsonInternal(Object value, JsonWriter gen, JsonWriteOptions options) throws IOException { if (value == null) { - gen.writeNull(); + gen.nullValue(); } else if (value instanceof Number) { - gen.writeNumber(((Number) value).doubleValue()); + gen.jsonValue(value); } else if (value instanceof Boolean) { - gen.writeBoolean((Boolean) value); + gen.value((Boolean) value); } else if (value instanceof String) { - gen.writeString((String) value); - - // } else if (o instanceof JsonElement) { - + gen.value((String) value); } else if (value instanceof Map) { toJsonFromMap((Map) value, gen, options); - } else if (value instanceof Collection) { toJsonFromCollection((Collection) value, null, gen, options); - } else if (value instanceof EntityBean) { BeanDescriptor d = getDescriptor(value.getClass()); WriteJson writeJson = createWriteJson(gen, options); d.jsonWrite(writeJson, (EntityBean) value, null); - } else { jsonScalar.write(gen, value); } @@ -385,8 +379,8 @@ public final class DJsonContext implements SpiJsonContext { @Override public SpiJsonReader createJsonRead(BeanType beanType, String json) { BeanDescriptor desc = (BeanDescriptor) beanType; - JsonParser parser = createParser(new StringReader(json)); - return new ReadJson(desc, parser, null, defaultObjectMapper, false); + JsonReader parser = createParser(json); + return new ReadJson(desc, parser, null, defaultObjectMapper, false, stream()); } @Override @@ -395,11 +389,11 @@ public final class DJsonContext implements SpiJsonContext { } @Override - public SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options) { + public SpiJsonWriter createJsonWriter(JsonWriter gen, JsonWriteOptions options) { return createWriteJson(gen, options); } - private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) { + private WriteJson createWriteJson(JsonWriter gen, JsonWriteOptions options) { FetchPath pathProps = (options == null) ? null : options.getPathProperties(); Map> visitors = (options == null) ? null : options.getVisitorMap(); return new WriteJson(server, @@ -411,46 +405,50 @@ public final class DJsonContext implements SpiJsonContext { options == null || options.isIncludeLoadedImplicit()); } - private void toJsonFromCollection(Collection collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException { + private void toJsonFromCollection(Collection collection, String key, JsonWriter gen, JsonWriteOptions options) throws IOException { if (key != null) { - gen.writeFieldName(key); + gen.name(key); } - gen.writeStartArray(); + gen.beginArray(); WriteJson writeJson = createWriteJson(gen, options); for (T bean : collection) { - BeanDescriptor d = getDescriptor(bean.getClass()); - d.jsonWrite(writeJson, (EntityBean) bean, null); + if (bean == null) { + gen.nullValue(); + } else if (bean instanceof EntityBean) { + BeanDescriptor d = getDescriptor(bean.getClass()); + d.jsonWrite(writeJson, (EntityBean) bean, null); + } else { + EJson.write(bean, gen); + } } - gen.writeEndArray(); + gen.endArray(); } - private void toJsonFromMap(Map map, JsonGenerator gen, JsonWriteOptions options) throws IOException { + private void toJsonFromMap(Map map, JsonWriter gen, JsonWriteOptions options) throws IOException { Set> entrySet = map.entrySet(); Iterator> it = entrySet.iterator(); WriteJson writeJson = createWriteJson(gen, options); - gen.writeStartObject(); + gen.beginObject(); while (it.hasNext()) { Entry entry = it.next(); String key = entry.getKey().toString(); Object value = entry.getValue(); if (value == null) { - gen.writeNullField(key); + gen.name(key); + gen.nullValue(); + } else if (value instanceof Collection) { + toJsonFromCollection((Collection) value, key, gen, options); + } else if (value instanceof EntityBean) { + BeanDescriptor d = getDescriptor(value.getClass()); + d.jsonWrite(writeJson, (EntityBean) value, key); } else { - if (value instanceof Collection) { - toJsonFromCollection((Collection) value, key, gen, options); - - } else if (value instanceof EntityBean) { - BeanDescriptor d = getDescriptor(value.getClass()); - d.jsonWrite(writeJson, (EntityBean) value, key); - - } else { - EJson.write(entry, gen); - } + gen.name(key); + EJson.write(value, gen); } } - gen.writeEndObject(); + gen.endObject(); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonScalar.java b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonScalar.java index cd3420773..d18e96a34 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonScalar.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/json/DJsonScalar.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.json; -import com.fasterxml.jackson.core.JsonGenerator; +import io.avaje.json.JsonWriter; import io.ebean.core.type.ScalarType; import io.ebeaninternal.server.type.TypeManager; @@ -19,21 +19,18 @@ public final class DJsonScalar { } @SuppressWarnings({ "unchecked", "rawtypes" }) - public void write(JsonGenerator gen, Object value) throws IOException { + public void write(JsonWriter gen, Object value) throws IOException { if (value instanceof String) { - gen.writeString((String) value); + gen.value((String) value); } else if (value instanceof List) { // expected for @DbArray values List list = (List)value; - gen.writeRaw('['); + gen.beginArray(); for (int i = 0; i < list.size(); i++) { - if (i > 0) { - gen.writeRaw(','); - } write(gen, list.get(i)); } - gen.writeRaw(']'); + gen.endArray(); } else { ScalarType scalarType = typeManager.type(value.getClass()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/json/ReadJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/json/ReadJson.java index ab9100c9f..181810c35 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/json/ReadJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/json/ReadJson.java @@ -1,8 +1,10 @@ package io.ebeaninternal.server.json; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.stream.BufferRecycleStrategy; +import io.avaje.json.stream.JsonStream; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; @@ -22,8 +24,9 @@ import java.util.Map; */ public final class ReadJson implements SpiJsonReader { + private final JsonStream jsonStream; private final BeanDescriptor rootDesc; - private final JsonParser parser; + private final JsonReader parser; private final PathStack pathStack; /** * Map of the JsonReadBeanVisitor keyed by path. @@ -38,9 +41,10 @@ public final class ReadJson implements SpiJsonReader { /** * Construct with parser and readOptions. */ - public ReadJson(BeanDescriptor desc, JsonParser parser, JsonReadOptions readOptions, Object objectMapper, boolean update) { + public ReadJson(BeanDescriptor desc, JsonReader parser, JsonReadOptions readOptions, Object objectMapper, boolean update, JsonStream jsonStream) { this.rootDesc = desc; this.parser = parser; + this.jsonStream = jsonStream; this.objectMapper = objectMapper; this.persistenceContext = initPersistenceContext(readOptions); this.loadContext = initLoadContext(desc, readOptions); @@ -54,7 +58,8 @@ public final class ReadJson implements SpiJsonReader { /** * Construct when transferring load context, persistence context, object mapper etc to a new ReadJson instance. */ - private ReadJson(JsonParser moreJson, ReadJson source) { + private ReadJson(JsonReader moreJson, ReadJson source) { + this.jsonStream = source.jsonStream; this.parser = moreJson; this.rootDesc = source.rootDesc; this.pathStack = source.pathStack; @@ -91,13 +96,22 @@ public final class ReadJson implements SpiJsonReader { } /** - * Return a new instance of ReadJson using the existing context but with a new JsonParser. + * Return a new instance of ReadJson using the existing context but with a new JsonReader. */ @Override - public SpiJsonReader forJson(JsonParser moreJson) { + public SpiJsonReader forJson(JsonReader moreJson) { return new ReadJson(moreJson, this); } + @Override + public SpiJsonReader forJson(String moreJson) { + JsonReader nestedReader = JsonStream.builder() + .bufferRecycling(BufferRecycleStrategy.NO_RECYCLING) + .build() + .reader(moreJson); + return new ReadJson(nestedReader, this); + } + /** * Add the bean to the persistence context. */ @@ -152,19 +166,19 @@ public final class ReadJson implements SpiJsonReader { } /** - * Return the JsonParser. + * Return the JsonReader. */ @Override - public JsonParser parser() { + public JsonReader parser() { return parser; } /** - * Return the next JsonToken from the underlying parser. + * Return the current token from the underlying parser. */ @Override - public JsonToken nextToken() throws IOException { - return parser.nextToken(); + public Token nextToken() throws IOException { + return parser.currentToken(); } /** @@ -209,7 +223,7 @@ public final class ReadJson implements SpiJsonReader { */ @Override public Object readValueUsingObjectMapper(Class propertyType) throws IOException { - return mapper().readValue(parser, propertyType); + return mapper().readValue(parser.readRaw(), propertyType); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/json/WriteJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/json/WriteJson.java index f5b583822..b11242d52 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/json/WriteJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/json/WriteJson.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.json; -import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; +import io.avaje.json.JsonWriter; import io.ebean.FetchPath; import io.ebean.bean.EntityBean; import io.ebean.config.JsonConfig; @@ -24,7 +24,7 @@ import java.util.Set; public final class WriteJson implements SpiJsonWriter { private final SpiEbeanServer server; - private final JsonGenerator generator; + private final JsonWriter generator; private final FetchPath fetchPath; private final Map> visitors; private final PathStack pathStack; @@ -37,7 +37,7 @@ public final class WriteJson implements SpiJsonWriter { /** * Construct for full bean use (normal). */ - public WriteJson(SpiEbeanServer server, JsonGenerator generator, FetchPath fetchPath, + public WriteJson(SpiEbeanServer server, JsonWriter generator, FetchPath fetchPath, Map> visitors, Object objectMapper, JsonConfig.Include include, boolean includeLoadedImplicit) { @@ -50,12 +50,14 @@ public final class WriteJson implements SpiJsonWriter { this.includeLoadedImplicit = includeLoadedImplicit; this.parentBeans = new ArrayStack<>(); this.pathStack = new PathStack(); + this.generator.serializeNulls(isIncludeNull()); + this.generator.serializeEmpty(isIncludeEmpty()); } /** * Construct for Json scalar use. */ - public WriteJson(JsonGenerator generator, JsonConfig.Include include) { + public WriteJson(JsonWriter generator, JsonConfig.Include include) { this.generator = generator; this.include = include; this.includeLoadedImplicit = true; @@ -65,6 +67,8 @@ public final class WriteJson implements SpiJsonWriter { this.objectMapper = null; this.parentBeans = null; this.pathStack = null; + this.generator.serializeNulls(isIncludeNull()); + this.generator.serializeEmpty(isIncludeEmpty()); } /** @@ -84,7 +88,7 @@ public final class WriteJson implements SpiJsonWriter { } @Override - public JsonGenerator gen() { + public JsonWriter gen() { return generator; } @@ -95,171 +99,113 @@ public final class WriteJson implements SpiJsonWriter { @Override public void writeStartObject(String key) { - try { - if (key != null) { - generator.writeFieldName(key); - } - generator.writeStartObject(); - } catch (IOException e) { - throw new JsonIOException(e); + if (key != null) { + generator.name(key); } + generator.beginObject(); } @Override public void writeStartObject() { - try { - generator.writeStartObject(); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.beginObject(); } @Override public void writeEndObject() { - try { - generator.writeEndObject(); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.endObject(); } @Override public void writeStartArray(String key) { - try { - if (key != null) { - generator.writeFieldName(key); - } - generator.writeStartArray(); - } catch (IOException e) { - throw new JsonIOException(e); + if (key != null) { + generator.name(key); } + generator.beginArray(); } @Override public void writeStartArray() { - try { - generator.writeStartArray(); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.beginArray(); } @Override public void writeEndArray() { - try { - generator.writeEndArray(); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.endArray(); } @Override public void writeRaw(String text) { - try { - generator.writeRaw(text); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.rawChunkStart(); + generator.rawChunk(text); + generator.rawChunkEnd(); } @Override public void writeRawValue(String text) { - try { - generator.writeRawValue(text); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.rawValue(text); } @Override public void writeFieldName(String name) { - try { - generator.writeFieldName(name); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); } @Override public void writeNullField(String name) { if (isIncludeNull()) { - try { - generator.writeNullField(name); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.nullValue(); } } @Override public void writeNumberField(String name, long value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeNumberField(String name, double value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeNumberField(String name, int value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeNumberField(String name, short value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeNumberField(String name, float value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value((double) value); } @Override public void writeNumberField(String name, BigDecimal value) { - try { - generator.writeNumberField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeStringField(String name, String value) { - try { - generator.writeStringField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeBinary(InputStream is, int length) { try { - generator.writeBinary(is, length); + generator.value(is.readNBytes(length)); } catch (IOException e) { throw new JsonIOException(e); } @@ -267,83 +213,49 @@ public final class WriteJson implements SpiJsonWriter { @Override public void writeBinaryField(String name, byte[] value) { - try { - generator.writeBinaryField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeBooleanField(String name, boolean value) { - try { - generator.writeBooleanField(name, value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.name(name); + generator.value(value); } @Override public void writeBoolean(boolean value) { - try { - generator.writeBoolean(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeString(String value) { - try { - generator.writeString(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeNumber(int value) { - try { - generator.writeNumber(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeNumber(long value) { - try { - generator.writeNumber(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeNumber(double value) { - try { - generator.writeNumber(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeNumber(BigDecimal value) { - try { - generator.writeNumber(value); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.value(value); } @Override public void writeNull() { - try { - generator.writeNull(); - } catch (IOException e) { - throw new JsonIOException(e); - } + generator.nullValue(); } @Override @@ -375,55 +287,39 @@ public final class WriteJson implements SpiJsonWriter { @Override public void beginAssocMany(String key) { - try { - pathStack.pushPathKey(key); - if (key != null) { - generator.writeFieldName(key); - } - generator.writeStartArray(); - } catch (IOException e) { - throw new JsonIOException(e); + pathStack.pushPathKey(key); + if (key != null) { + generator.name(key); } + generator.beginArray(); } @Override public void endAssocMany() { - try { - pathStack.pop(); - generator.writeEndArray(); - } catch (IOException e) { - throw new JsonIOException(e); - } + pathStack.pop(); + generator.endArray(); } @Override public void beginAssocManyMap(String key, boolean elementCollection) { - try { - pathStack.pushPathKey(key); - if (key != null) { - generator.writeFieldName(key); - } - if (elementCollection) { - generator.writeStartObject(); - } else { - generator.writeStartArray(); - } - } catch (IOException e) { - throw new JsonIOException(e); + pathStack.pushPathKey(key); + if (key != null) { + generator.name(key); + } + if (elementCollection) { + generator.beginObject(); + } else { + generator.beginArray(); } } @Override public void endAssocManyMap(boolean elementCollection) { - try { - pathStack.pop(); - if (elementCollection) { - generator.writeEndObject(); - } else { - generator.writeEndArray(); - } - } catch (IOException e) { - throw new JsonIOException(e); + pathStack.pop(); + if (elementCollection) { + generator.endObject(); + } else { + generator.endArray(); } } @@ -465,8 +361,8 @@ public final class WriteJson implements SpiJsonWriter { } } try { - generator.writeFieldName(name); - objectMapper().writeValue(generator, value); + generator.name(name); + generator.rawValue(objectMapper().writeValueAsString(value)); } catch (IOException e) { throw new JsonIOException(e); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/readaudit/DefaultReadAuditLogger.java b/ebean-core/src/main/java/io/ebeaninternal/server/readaudit/DefaultReadAuditLogger.java index 785dd57b5..167b2ce86 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/readaudit/DefaultReadAuditLogger.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/readaudit/DefaultReadAuditLogger.java @@ -1,8 +1,8 @@ package io.ebeaninternal.server.readaudit; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; import io.avaje.applog.AppLog; +import io.avaje.json.JsonWriter; +import io.avaje.json.stream.JsonStream; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditQueryPlan; import io.ebean.event.readaudit.ReadEvent; @@ -24,7 +24,7 @@ public class DefaultReadAuditLogger implements ReadAuditLogger { private static final System.Logger queryLogger = AppLog.getLogger("io.ebean.ReadAuditQuery"); private static final System.Logger auditLogger = AppLog.getLogger("io.ebean.ReadAudit"); - protected final JsonFactory jsonFactory = new JsonFactory(); + protected final JsonStream jsonStream = JsonStream.builder().build(); protected final int defaultQueryBuffer = 500; protected final int defaultReadBuffer = 150; @@ -34,25 +34,26 @@ public class DefaultReadAuditLogger implements ReadAuditLogger { @Override public void queryPlan(ReadAuditQueryPlan queryPlan) { StringWriter writer = new StringWriter(defaultQueryBuffer); - try (JsonGenerator gen = jsonFactory.createGenerator(writer)) { - gen.writeStartObject(); + try (JsonWriter gen = jsonStream.writer(writer)) { + gen.beginObject(); String beanType = queryPlan.getBeanType(); if (beanType != null) { - gen.writeStringField("beanType", beanType); + gen.name("beanType"); + gen.value(beanType); } String queryKey = queryPlan.getQueryKey(); if (queryKey != null) { - gen.writeStringField("queryKey", queryKey); + gen.name("queryKey"); + gen.value(queryKey); } String sql = queryPlan.getSql(); if (sql != null) { - gen.writeStringField("sql", sql); + gen.name("sql"); + gen.value(sql); } - gen.writeEndObject(); + gen.endObject(); gen.flush(); queryLogger.log(INFO, writer.toString()); - } catch (IOException e) { - CoreLog.log.log(ERROR, "Error writing Read audit event", e); } } @@ -73,56 +74,64 @@ public class DefaultReadAuditLogger implements ReadAuditLogger { } protected void writeEvent(ReadEvent event) { - try { - StringWriter writer = new StringWriter(defaultReadBuffer); - JsonGenerator gen = jsonFactory.createGenerator(writer); + StringWriter writer = new StringWriter(defaultReadBuffer); + try (JsonWriter gen = jsonStream.writer(writer)) { writeDetails(gen, event); - auditLogger.log(INFO, writer.toString()); } catch (IOException e) { CoreLog.log.log(ERROR, "Error writing Read audit event", e); + return; } + auditLogger.log(INFO, writer.toString()); } /** * Write the details for the read bean or read many beans event. */ - protected void writeDetails(JsonGenerator gen, ReadEvent event) throws IOException { - gen.writeStartObject(); + protected void writeDetails(JsonWriter gen, ReadEvent event) throws IOException { + gen.beginObject(); String source = event.getSource(); if (source != null) { - gen.writeStringField("source", source); + gen.name("source"); + gen.value(source); } String userId = event.getUserId(); if (userId != null) { - gen.writeStringField("userId", userId); + gen.name("userId"); + gen.value(userId); } String userIpAddress = event.getUserIpAddress(); if (userIpAddress != null) { - gen.writeStringField("userIpAddress", userIpAddress); + gen.name("userIpAddress"); + gen.value(userIpAddress); } Map userContext = event.getUserContext(); if (userContext != null && !userContext.isEmpty()) { - gen.writeObjectFieldStart("userContext"); + gen.name("userContext"); + gen.beginObject(); for (Map.Entry entry : userContext.entrySet()) { - gen.writeStringField(entry.getKey(), entry.getValue()); + gen.name(entry.getKey()); + gen.value(entry.getValue()); } - gen.writeEndObject(); + gen.endObject(); } - gen.writeNumberField("eventTime", event.getEventTime()); - gen.writeStringField("beanType", event.getBeanType()); - gen.writeStringField("queryKey", event.getQueryKey()); - gen.writeStringField("bindLog", event.getBindLog()); + gen.name("eventTime"); + gen.value(event.getEventTime()); + gen.name("beanType"); + gen.value(event.getBeanType()); + gen.name("queryKey"); + gen.value(event.getQueryKey()); + gen.name("bindLog"); + gen.value(event.getBindLog()); Object id = event.getId(); if (id != null) { - gen.writeFieldName("id"); + gen.name("id"); EJson.write(id, gen); } else { - gen.writeFieldName("ids"); + gen.name("ids"); EJson.writeCollection(event.getIds(), gen); } - gen.writeEndObject(); + gen.endObject(); gen.flush(); - gen.close(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java index 59e85b54a..e976a5279 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java @@ -1,8 +1,8 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarType; @@ -173,12 +173,16 @@ class ScalarTypeArrayList extends ScalarTypeArrayBase implements ScalarTyp } @Override - public List jsonRead(JsonParser parser) throws IOException { - return convert(EJson.parseList(parser, parser.getCurrentToken())); + public List jsonRead(JsonReader parser) throws IOException { + if (parser.isNullValue()) { + return null; + } + List rawList = EJson.parseList(parser, parser.currentToken()); + return convert(rawList); } @Override - public void jsonWrite(JsonGenerator writer, List value) throws IOException { + public void jsonWrite(JsonWriter writer, List value) throws IOException { EJson.write(value, writer); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java index d1034be93..6ae1d817c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java @@ -1,8 +1,8 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarType; @@ -152,12 +152,16 @@ class ScalarTypeArraySet extends ScalarTypeArrayBase implements ScalarTypeA } @Override - public Set jsonRead(JsonParser parser) throws IOException { - return convert(EJson.parseList(parser, parser.getCurrentToken())); + public Set jsonRead(JsonReader parser) throws IOException { + if (parser.isNullValue()) { + return null; + } + List rawList = EJson.parseList(parser, parser.currentToken()); + return convert(rawList); } @Override - public void jsonWrite(JsonGenerator writer, Set value) throws IOException { + public void jsonWrite(JsonWriter writer, Set value) throws IOException { EJson.write(value, writer); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBigDecimal.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBigDecimal.java index c771ff59b..802e0051a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBigDecimal.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBigDecimal.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -78,13 +78,13 @@ class ScalarTypeBigDecimal extends ScalarTypeBase { } @Override - public BigDecimal jsonRead(JsonParser parser) throws IOException { - return parser.getDecimalValue(); + public BigDecimal jsonRead(JsonReader parser) throws IOException { + return parser.readDecimal(); } @Override - public void jsonWrite(JsonGenerator writer, BigDecimal value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, BigDecimal value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBoolean.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBoolean.java index 62aa6c553..690a46b90 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBoolean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBoolean.java @@ -1,8 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -348,13 +347,39 @@ public final class ScalarTypeBoolean { } @Override - public Boolean jsonRead(JsonParser parser) { - return JsonToken.VALUE_TRUE == parser.getCurrentToken() ? Boolean.TRUE : Boolean.FALSE; + public Boolean jsonRead(JsonReader parser) { + if (parser.isNullValue()) { + return null; + } + var token = parser.currentToken(); + if (token == JsonReader.Token.BOOLEAN) { + return parser.readBoolean(); + } + if (token == JsonReader.Token.NUMBER) { + return parser.readDecimal().intValue() == 1; + } + if (token == JsonReader.Token.STRING) { + return parse(parser.readString()); + } + String raw = parser.readRaw(); + if (raw == null || "null".equals(raw)) { + return null; + } + if ("true".equals(raw) || "1".equals(raw)) { + return Boolean.TRUE; + } + if ("false".equals(raw) || "0".equals(raw)) { + return Boolean.FALSE; + } + if (raw.length() > 1 && raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') { + return parse(raw.substring(1, raw.length() - 1)); + } + return parse(raw); } @Override - public void jsonWrite(JsonGenerator writer, Boolean value) throws IOException { - writer.writeBoolean(value); + public void jsonWrite(JsonWriter writer, Boolean value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeByte.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeByte.java index e01bc625e..c087e4720 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeByte.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeByte.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -9,7 +9,6 @@ import io.ebean.core.type.ScalarTypeBase; import io.ebean.text.TextException; import io.ebean.core.type.BasicTypeConverter; -import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -55,15 +54,13 @@ final class ScalarTypeByte extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator writer, Byte value) throws IOException { - writer.writeBinary(new byte[]{value}); + public void jsonWrite(JsonWriter writer, Byte value) throws IOException { + writer.value(new byte[]{value}); } @Override - public Byte jsonRead(JsonParser parser) throws IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - parser.readBinaryValue(os); - byte[] bytes = os.toByteArray(); + public Byte jsonRead(JsonReader parser) throws IOException { + byte[] bytes = parser.readBinary(); if (bytes.length == 0) { return null; } else { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesBase.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesBase.java index a5647e8a7..1165c5c52 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesBase.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesBase.java @@ -1,13 +1,12 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarTypeBase; import io.ebean.text.TextException; -import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -85,15 +84,13 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator writer, byte[] value) throws IOException { - writer.writeBinary(value); + public void jsonWrite(JsonWriter writer, byte[] value) throws IOException { + writer.value(value); } @Override - public byte[] jsonRead(JsonParser parser) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(500); - parser.readBinaryValue(out); - return out.toByteArray(); + public byte[] jsonRead(JsonReader parser) throws IOException { + return parser.readBinary(); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java index 3fdc0f5b2..416fa17f0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java @@ -1,13 +1,12 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarType; -import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -58,15 +57,13 @@ public final class ScalarTypeBytesEncrypted implements ScalarType { } @Override - public void jsonWrite(JsonGenerator writer, byte[] value) throws IOException { - writer.writeBinary(value); + public void jsonWrite(JsonWriter writer, byte[] value) throws IOException { + writer.value(value); } @Override - public byte[] jsonRead(JsonParser parser) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(500); - parser.readBinaryValue(out); - return out.toByteArray(); + public byte[] jsonRead(JsonReader parser) throws IOException { + return parser.readBinary(); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCharArray.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCharArray.java index ff74e0cfe..2f50d3696 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCharArray.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCharArray.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarTypeBaseVarchar; @@ -72,7 +72,7 @@ final class ScalarTypeCharArray extends ScalarTypeBaseVarchar { } @Override - public char[] jsonRead(JsonParser parser) throws IOException { - return parser.getValueAsString().toCharArray(); + public char[] jsonRead(JsonReader parser) throws IOException { + return parser.readString().toCharArray(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDouble.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDouble.java index 3fe5deba0..6b21c1a1c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDouble.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDouble.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -77,13 +77,13 @@ final class ScalarTypeDouble extends ScalarTypeBase { } @Override - public Double jsonRead(JsonParser parser) throws IOException { - return parser.getDoubleValue(); + public Double jsonRead(JsonReader parser) throws IOException { + return parser.readDouble(); } @Override - public void jsonWrite(JsonGenerator writer, Double value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, Double value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDuration.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDuration.java index 1589a22af..a75f53bac 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDuration.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeDuration.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.*; import java.io.DataInput; @@ -90,13 +90,13 @@ class ScalarTypeDuration extends ScalarTypeBase { } @Override - public Duration jsonRead(JsonParser parser) throws IOException { - return Duration.parse(parser.getValueAsString()); + public Duration jsonRead(JsonReader parser) throws IOException { + return Duration.parse(parser.readString()); } @Override - public void jsonWrite(JsonGenerator writer, Duration value) throws IOException { - writer.writeString(value.toString()); + public void jsonWrite(JsonWriter writer, Duration value) throws IOException { + writer.value(value.toString()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java index 627d32a65..4d37a007d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -120,12 +120,12 @@ public final class ScalarTypeEncryptedWrapper implements ScalarType, Local } @Override - public T jsonRead(JsonParser parser) throws IOException { + public T jsonRead(JsonReader parser) throws IOException { return wrapped.jsonRead(parser); } @Override - public void jsonWrite(JsonGenerator writer, T value) throws IOException { + public void jsonWrite(JsonWriter writer, T value) throws IOException { wrapped.jsonWrite(writer, value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEnumStandard.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEnumStandard.java index 227520fbf..2a0476921 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEnumStandard.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeEnumStandard.java @@ -1,11 +1,11 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarTypeBase; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import jakarta.persistence.EnumType; import java.io.DataInput; @@ -228,20 +228,19 @@ final class ScalarTypeEnumStandard { } @Override - public Object jsonRead(JsonParser parser) throws IOException { - if (parser.getCodec() != null) { - return parser.readValueAs(enumType); - } else { - return parse(parser.getValueAsString()); + public Object jsonRead(JsonReader parser) throws IOException { + if (parser.isNullValue()) { + return null; } + return parse(parser.readString()); } @Override - public void jsonWrite(JsonGenerator writer, Object value) throws IOException { - if (writer.getCodec() != null) { - writer.writeObject(value); + public void jsonWrite(JsonWriter writer, Object value) throws IOException { + if (value == null) { + writer.nullValue(); } else { - writer.writeString(formatValue(value)); + writer.value(formatValue(value)); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java index 5e4ad5df8..01ab9747d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -102,16 +102,17 @@ final class ScalarTypeFile extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator writer, File value) throws IOException { - InputStream is = getInputStream(value); - writer.writeBinary(is, (int) value.length()); + public void jsonWrite(JsonWriter writer, File value) throws IOException { + try (InputStream is = getInputStream(value)) { + writer.value(is.readAllBytes()); + } } @Override - public File jsonRead(JsonParser parser) throws IOException { + public File jsonRead(JsonReader parser) throws IOException { File tempFile = File.createTempFile(prefix, suffix, directory); try (OutputStream os = getOutputStream(tempFile)) { - parser.readBinaryValue(os); + os.write(parser.readBinary()); os.flush(); } return tempFile; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFloat.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFloat.java index facea6618..6e2f1acdc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFloat.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFloat.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -77,13 +77,13 @@ final class ScalarTypeFloat extends ScalarTypeBase { } @Override - public Float jsonRead(JsonParser parser) throws IOException { - return parser.getFloatValue(); + public Float jsonRead(JsonReader parser) throws IOException { + return (float) parser.readDouble(); } @Override - public void jsonWrite(JsonGenerator writer, Float value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, Float value) throws IOException { + writer.value(value.doubleValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeInteger.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeInteger.java index aea3b8d16..97ea05207 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeInteger.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeInteger.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -87,13 +87,13 @@ final class ScalarTypeInteger extends ScalarTypeBase { } @Override - public Integer jsonRead(JsonParser parser) throws IOException { - return parser.getIntValue(); + public Integer jsonRead(JsonReader parser) throws IOException { + return parser.readInt(); } @Override - public void jsonWrite(JsonGenerator writer, Integer value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, Integer value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java index 8d43c7d38..51da2bbc2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonList.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.core.type.*; import io.ebean.text.TextException; @@ -182,12 +182,12 @@ final class ScalarTypeJsonList { } @Override - public final List jsonRead(JsonParser parser) throws IOException { - return EJson.parseList(parser, parser.getCurrentToken()); + public final List jsonRead(JsonReader parser) throws IOException { + return EJson.parseList(parser, parser.currentToken()); } @Override - public final void jsonWrite(JsonGenerator writer, List value) throws IOException { + public final void jsonWrite(JsonWriter writer, List value) throws IOException { EJson.write(value, writer); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java index a97b9911c..5bc0a4bef 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonMap.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; @@ -235,13 +235,13 @@ abstract class ScalarTypeJsonMap extends ScalarTypeBase { } @Override - public final void jsonWrite(JsonGenerator writer, Map value) throws IOException { + public final void jsonWrite(JsonWriter writer, Map value) throws IOException { EJson.write(value, writer); } @Override - public final Map jsonRead(JsonParser parser) throws IOException { - return EJson.parseObject(parser, parser.getCurrentToken()); + public final Map jsonRead(JsonReader parser) throws IOException { + return EJson.parseObject(parser, parser.currentToken()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java index 3997c374a..d094001f0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonSet.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.core.type.*; import io.ebean.text.TextException; @@ -179,12 +179,12 @@ final class ScalarTypeJsonSet { } @Override - public final Set jsonRead(JsonParser parser) throws IOException { - return convertList(EJson.parseList(parser, parser.getCurrentToken())); + public final Set jsonRead(JsonReader parser) throws IOException { + return convertList(EJson.parseList(parser, parser.currentToken())); } @Override - public final void jsonWrite(JsonGenerator writer, Set value) throws IOException { + public final void jsonWrite(JsonWriter writer, Set value) throws IOException { EJson.write(value, writer); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTime.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTime.java index eb84613ea..0dde36f04 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTime.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTime.java @@ -1,7 +1,8 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonReader.Token; +import io.avaje.json.JsonWriter; import io.ebean.config.JsonConfig; import io.ebean.config.dbplatform.ExtraDbTypes; import io.ebean.core.type.ScalarTypeBaseDateTime; @@ -48,13 +49,19 @@ final class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime { } @Override - public LocalTime jsonRead(JsonParser parser) throws IOException { - return LocalTime.parse(parser.getValueAsString()); + public LocalTime jsonRead(JsonReader parser) throws IOException { + return LocalTime.parse(parser.readString()); } @Override - public void jsonWrite(JsonGenerator writer, LocalTime value) throws IOException { - writer.writeString(value.toString()); + public void jsonWrite(JsonWriter writer, LocalTime value) throws IOException { + writer.value(value.toString()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLong.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLong.java index b04a0a936..6a1fb4221 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLong.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeLong.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -87,13 +87,13 @@ final class ScalarTypeLong extends ScalarTypeBase { } @Override - public Long jsonRead(JsonParser parser) throws IOException { - return parser.getLongValue(); + public Long jsonRead(JsonReader parser) throws IOException { + return parser.readLong(); } @Override - public void jsonWrite(JsonGenerator writer, Long value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, Long value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMathBigInteger.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMathBigInteger.java index 29ee7316c..63246fb74 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMathBigInteger.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMathBigInteger.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -82,13 +82,13 @@ final class ScalarTypeMathBigInteger extends ScalarTypeBase { } @Override - public BigInteger jsonRead(JsonParser parser) throws IOException { - return parser.getDecimalValue().toBigInteger(); + public BigInteger jsonRead(JsonReader parser) throws IOException { + return parser.readBigInteger(); } @Override - public void jsonWrite(JsonGenerator writer, BigInteger value) throws IOException { - writer.writeNumber(value.longValue()); + public void jsonWrite(JsonWriter writer, BigInteger value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMonthDay.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMonthDay.java index 95d339bc5..e777f3104 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMonthDay.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeMonthDay.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -102,13 +102,13 @@ final class ScalarTypeMonthDay extends ScalarTypeBase { } @Override - public MonthDay jsonRead(JsonParser parser) throws IOException { - return parse(parser.getValueAsString()); + public MonthDay jsonRead(JsonReader parser) throws IOException { + return parse(parser.readString()); } @Override - public void jsonWrite(JsonGenerator writer, MonthDay value) throws IOException { - writer.writeString(format(value)); + public void jsonWrite(JsonWriter writer, MonthDay value) throws IOException { + writer.value(format(value)); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java index 235ea7334..629dd7e75 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeNotFound.java @@ -5,8 +5,8 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; @@ -84,12 +84,12 @@ class ScalarTypeNotFound implements ScalarType { } @Override - public Void jsonRead(JsonParser parser) throws IOException { + public Void jsonRead(JsonReader parser) throws IOException { throw new UnsupportedOperationException(); } @Override - public void jsonWrite(JsonGenerator writer, Void value) throws IOException { + public void jsonWrite(JsonWriter writer, Void value) throws IOException { throw new UnsupportedOperationException(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java index 92bd4a450..5eddc164a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypePostgresHstore.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; @@ -102,16 +102,16 @@ final class ScalarTypePostgresHstore extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator writer, Map value) throws IOException { + public void jsonWrite(JsonWriter writer, Map value) throws IOException { EJson.write(value, writer); } @Override - public Map jsonRead(JsonParser parser) throws IOException { + public Map jsonRead(JsonReader parser) throws IOException { // at this point the BeanProperty has read the START_OBJECT token // to check for a null value. Pass the START_OBJECT token through to // the EJson parsing so that it knows the first token has been read - return EJson.parseObject(parser, parser.getCurrentToken()); + return EJson.parseObject(parser, parser.currentToken()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeShort.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeShort.java index 059b68a1a..81fa64814 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeShort.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeShort.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -77,13 +77,13 @@ final class ScalarTypeShort extends ScalarTypeBase { } @Override - public Short jsonRead(JsonParser parser) throws IOException { - return parser.getShortValue(); + public Short jsonRead(JsonReader parser) throws IOException { + return (short) parser.readInt(); } @Override - public void jsonWrite(JsonGenerator writer, Short value) throws IOException { - writer.writeNumber(value); + public void jsonWrite(JsonWriter writer, Short value) throws IOException { + writer.value(value.intValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeStringBase.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeStringBase.java index 4b260e096..611278e7f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeStringBase.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeStringBase.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -77,13 +77,16 @@ abstract class ScalarTypeStringBase extends ScalarTypeBase { } @Override - public String jsonRead(JsonParser parser) throws IOException { - return parser.getValueAsString(); + public String jsonRead(JsonReader parser) throws IOException { + if (parser.isNullValue()) { + return null; + } + return parser.readString(); } @Override - public void jsonWrite(JsonGenerator writer, String value) throws IOException { - writer.writeString(value); + public void jsonWrite(JsonWriter writer, String value) throws IOException { + writer.value(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTime.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTime.java index a301da7ed..8490b52be 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTime.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTime.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.type; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -83,13 +83,13 @@ final class ScalarTypeTime extends ScalarTypeBase