This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/master by this push: new d721074 JOHNZON-285 enable to support a fastpath for a reader implementing Supplier<JsonObject> and a writer implementing a Consumer<JsonValue> d721074 is described below commit d721074adc406c2e5e3112e939379c5989f247a3 Author: Romain Manni-Bucau <rmannibu...@apache.org> AuthorDate: Tue Oct 1 19:03:45 2019 +0200 JOHNZON-285 enable to support a fastpath for a reader implementing Supplier<JsonObject> and a writer implementing a Consumer<JsonValue> --- .../org/apache/johnzon/jsonb/JohnzonJsonb.java | 69 ++++++++++++++++++---- .../johnzon/jsonb/extension/JsonValueReader.java | 26 ++++++-- .../johnzon/jsonb/extension/JsonValueWriter.java | 27 +++++++-- .../java/org/apache/johnzon/mapper/Mapper.java | 13 +++- src/site/markdown/index.md | 2 +- 5 files changed, 113 insertions(+), 24 deletions(-) diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java index 7eae65e..ba2d8b2 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java @@ -20,8 +20,6 @@ package org.apache.johnzon.jsonb; import org.apache.johnzon.mapper.util.ArrayUtil; import org.apache.johnzon.jsonb.api.experimental.JsonbExtension; -import org.apache.johnzon.jsonb.extension.JsonValueReader; -import org.apache.johnzon.jsonb.extension.JsonValueWriter; import org.apache.johnzon.mapper.JsonObjectGenerator; import org.apache.johnzon.mapper.Mapper; import org.apache.johnzon.mapper.MapperException; @@ -43,17 +41,21 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigInteger; import java.util.Collection; +import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Stream; -// TODO: Optional handling for lists (and arrays)? public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { private final Mapper delegate; private final boolean ijson; private final Consumer<JohnzonJsonb> onClose; + private final Map<Class<?>, Boolean> structureAwareIo = new ConcurrentHashMap<>(); public JohnzonJsonb(final Mapper build, final boolean ijson, final Consumer<JohnzonJsonb> onClose) { this.delegate = build; @@ -143,18 +145,28 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { @Override public <T> T fromJson(final Reader reader, final Class<T> type) throws JsonbException { - if (JsonValueReader.class.isInstance(reader)) { - return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), type); - } - + final boolean valueProvider = isValueProvider(reader); try { if (isArray(type)) { + if (valueProvider) { + return delegate.readObject(((Supplier<JsonValue>) reader).get(), type); + } return delegate.readTypedArray(reader, type.getComponentType(), type); } else if (JsonArray.class == type) { + if (valueProvider) { + return delegate.readObject(((Supplier<JsonValue>) reader).get(), type); + } return (T) delegate.readJsonArray(reader); } else if (Collection.class.isAssignableFrom(type)) { + if (valueProvider) { + return delegate.readObject(((Supplier<JsonValue>) reader).get(), type); + } return (T) delegate.readCollection(reader, new JohnzonParameterizedType(type, Object.class)); } + if (valueProvider) { + return delegate.readObject(((Supplier<JsonValue>) reader).get(), type); + } + final Type mappingType = unwrapPrimitiveOptional(type); final Object object = delegate.readObject(reader, mappingType); if (mappingType != type) { @@ -168,8 +180,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { @Override public <T> T fromJson(final Reader reader, final Type runtimeType) throws JsonbException { - if (JsonValueReader.class.isInstance(reader)) { - return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), runtimeType); + if (isValueProvider(reader)) { + return delegate.readObject(((Supplier<JsonStructure>) reader).get(), runtimeType); } try { @@ -323,8 +335,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { @Override public void toJson(final Object inObject, final Writer writer) throws JsonbException { - if (JsonValueWriter.class.isInstance(writer)) { - JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject)); + if (isValueConsumer(writer)) { + Consumer.class.cast(writer).accept(delegate.toStructure(inObject)); return; } @@ -342,8 +354,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { @Override public void toJson(final Object inObject, final Type runtimeType, final Writer writer) throws JsonbException { - if (JsonValueWriter.class.isInstance(writer)) { - JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject)); + if (isValueConsumer(writer)) { + Consumer.class.cast(writer).accept(delegate.toStructure(inObject)); return; } @@ -520,4 +532,35 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { return jsonObjectGenerator.getResult(); } } + + private boolean isValueProvider(final Reader reader) { + final Class<? extends Reader> key = reader.getClass(); + Boolean exists = structureAwareIo.get(key); + if (exists == null) { + exists = matchesType(key, Supplier.class); + structureAwareIo.putIfAbsent(key, exists); + } + return exists; + } + + private boolean isValueConsumer(final Writer writer) { + final Class<? extends Writer> key = writer.getClass(); + Boolean exists = structureAwareIo.get(key); + if (exists == null) { + exists = matchesType(writer.getClass(), Consumer.class); + structureAwareIo.putIfAbsent(key, exists); + } + return exists; + } + + private boolean matchesType(final Class<?> type, final Class<?> rawType) { + return rawType.isAssignableFrom(type) && + Stream.of(type.getGenericInterfaces()) + .filter(ParameterizedType.class::isInstance) + .map(ParameterizedType.class::cast) + .anyMatch(pt -> pt.getRawType() == rawType && + pt.getActualTypeArguments().length == 1 && + Class.class.isInstance(pt.getActualTypeArguments()[0]) && + JsonValue.class.isAssignableFrom(Class.class.cast(pt.getActualTypeArguments()[0]))); + } } diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java index 222a6e0..c4899d3 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java @@ -18,12 +18,17 @@ */ package org.apache.johnzon.jsonb.extension; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; import javax.json.JsonStructure; -public class JsonValueReader<T> extends Reader { +public class JsonValueReader<T> extends Reader implements Supplier<JsonStructure> { private final JsonStructure input; + private ByteArrayInputStream fallbackDelegate; private T result; public JsonValueReader(final JsonStructure input) { @@ -32,22 +37,35 @@ public class JsonValueReader<T> extends Reader { @Override public int read(final char[] cbuf, final int off, final int len) { - throw new UnsupportedOperationException(); + if (fallbackDelegate == null) { + fallbackDelegate = new ByteArrayInputStream(input.toString().getBytes(StandardCharsets.UTF_8)); + } + return fallbackDelegate.read(); } @Override - public void close() { - // no-op + public void close() throws IOException { + if (fallbackDelegate != null) { + fallbackDelegate.close(); + } } + @Override + public JsonStructure get() { + return input; + } + + @Deprecated public JsonStructure getInput() { return input; } + @Deprecated public T getResult() { return result; } + @Deprecated public void setResult(final T result) { this.result = result; } diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java index a861658..fbb9404 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java @@ -18,18 +18,26 @@ */ package org.apache.johnzon.jsonb.extension; +import java.io.StringReader; import java.io.Writer; +import java.util.function.Consumer; +import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; +import javax.json.JsonReader; import javax.json.JsonValue; -public class JsonValueWriter extends Writer { +public class JsonValueWriter extends Writer implements Consumer<JsonValue> { private JsonValue result; + private StringBuilder fallbackOutput; @Override public void write(final char[] cbuf, final int off, final int len) { - throw new UnsupportedOperationException(); + if (fallbackOutput == null) { + fallbackOutput = new StringBuilder(); + } + fallbackOutput.append(cbuf, off, len); } @Override @@ -42,19 +50,30 @@ public class JsonValueWriter extends Writer { flush(); } + @Deprecated public void setResult(final JsonValue result) { this.result = result; } public JsonValue getResult() { + if (result == null && fallbackOutput != null) { + try (final JsonReader reader = Json.createReader(new StringReader(fallbackOutput.toString()))) { + result = reader.readValue(); + } + } return result; } public JsonObject getObject() { - return result.asJsonObject(); + return getResult().asJsonObject(); } public JsonArray getArray() { - return result.asJsonArray(); + return getResult().asJsonArray(); + } + + @Override + public void accept(final JsonValue jsonValue) { + this.result = jsonValue; } } diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java index e8ae5cc..4a9639c 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java @@ -250,11 +250,20 @@ public class Mapper implements Closeable { return writer.toString(); } - public <T> T readObject(final JsonStructure value, final Type clazz) { + public <T> T readObject(final JsonValue value, final Type clazz) { return new MappingParserImpl(config, mappings, new JsonReader() { @Override public JsonStructure read() { - return value; + switch (value.getValueType()) { + case STRING: + case FALSE: + case TRUE: + case NULL: + case NUMBER: + throw new UnsupportedOperationException("use readValue()"); + default: + return JsonStructure.class.cast(readValue()); + } } @Override diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 91f3f9f..764c722 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -326,7 +326,7 @@ Default will be picked from the current available API. Finally, this behavior on #### Integration with `JsonValue` -You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` and `JsonValueWriter`: +You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` - or any implementation of `Reader` implementing `Supplier<JsonStructure>` - and `JsonValueWriter` - or any implementation of `Writer` implementing `Consumer<JsonValue>` -: <pre class="prettyprint linenums"><![CDATA[ final JsonValueReader<Simple> reader = new JsonValueReader<>(Json.createObjectBuilder().add("value", "simple").build());