You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/04/04 09:15:55 UTC
[johnzon] 04/04: JOHNZON-207 some enhancement on Xavier's patch
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
commit 4bdb367a6b8c3ab9bbb23498b836d091c5598a72
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Thu Apr 4 11:15:37 2019 +0200
JOHNZON-207 some enhancement on Xavier's patch
---
.../main/java/org/apache/johnzon/core/Types.java | 69 ++++++--
.../java/org/apache/johnzon/core/TypesTest.java | 6 +-
.../org/apache/johnzon/jsonb/JohnzonBuilder.java | 5 +-
.../org/apache/johnzon/jsonb/JsonbAccessMode.java | 195 +++++++++++++--------
...MoreTests.java => SeriaizersRoundTripTest.java} | 116 +++++++++---
.../johnzon/mapper/DynamicMappingGenerator.java | 25 +--
.../johnzon/mapper/MappingGeneratorImpl.java | 17 +-
.../apache/johnzon/mapper/MappingParserImpl.java | 11 +-
8 files changed, 316 insertions(+), 128 deletions(-)
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
index 3e00e1d..d06d27f 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
@@ -41,11 +41,26 @@ public class Types {
* For the last example (UUIDStringConverter), nowhere in its hierarchy is a type directly implementing
* Converter[UUID, String] but this method is capable of reconstructing that information.
*/
- public static ParameterizedType findParameterizedType(Class<?> klass, Class<?> parameterizedClass) {
+ public ParameterizedType findParameterizedType(Class<?> klass, Class<?> parameterizedClass) {
return new ParameterizedTypeImpl(parameterizedClass, resolveArgumentTypes(klass, parameterizedClass));
}
- private static Type[] resolveArgumentTypes(Type type, Class<?> parameterizedClass) {
+ public Class<?> findParamType(final ParameterizedType type, final Class<?> expectedWrapper) {
+ if (type.getActualTypeArguments().length != 1) {
+ return null;
+ }
+ final Class<?> asClass = asClass(type.getRawType());
+ if (asClass == null || !expectedWrapper.isAssignableFrom(asClass)) {
+ return null;
+ }
+ return asClass(type.getActualTypeArguments()[0]);
+ }
+
+ public Class<?> asClass(final Type type) {
+ return Class.class.isInstance(type) ? Class.class.cast(type) : null;
+ }
+
+ private Type[] resolveArgumentTypes(Type type, Class<?> parameterizedClass) {
if (type instanceof Class<?>) {
return resolveArgumentTypes((Class<?>) type, parameterizedClass);
}
@@ -55,7 +70,7 @@ public class Types {
throw new IllegalArgumentException("Cannot resolve argument types from " + type.getClass().getSimpleName());
}
- private static Type[] resolveArgumentTypes(Class<?> type, Class<?> parameterizedClass) {
+ private Type[] resolveArgumentTypes(Class<?> type, Class<?> parameterizedClass) {
if (parameterizedClass.equals(type)) {
// May return Class[] instead of Type[], so copy it as a Type[] to avoid
// problems in visit(ParameterizedType)
@@ -74,7 +89,7 @@ public class Types {
throw new IllegalArgumentException(String.format("%s is not assignable from %s", type, parameterizedClass));
}
- private static Type[] resolveArgumentTypes(ParameterizedType type, Class<?> parameterizedClass) {
+ private Type[] resolveArgumentTypes(ParameterizedType type, Class<?> parameterizedClass) {
Class<?> rawType = (Class<?>) type.getRawType(); // always a Class
TypeVariable<?>[] typeVariables = rawType.getTypeParameters();
Type[] types = resolveArgumentTypes(rawType, parameterizedClass);
@@ -91,16 +106,12 @@ public class Types {
return types;
}
- private Types() {
- // no-op
- }
-
private static class ParameterizedTypeImpl implements ParameterizedType {
private final Type rawType;
private final Type[] arguments;
- ParameterizedTypeImpl(Type rawType, Type... arguments) {
+ private ParameterizedTypeImpl(final Type rawType, final Type... arguments) {
this.rawType = rawType;
this.arguments = arguments;
}
@@ -120,6 +131,44 @@ public class Types {
return arguments;
}
- // TODO equals, hashcode, toString if needed
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(arguments) ^ (rawType == null ? 0 : rawType.hashCode());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj instanceof ParameterizedType) {
+ final ParameterizedType that = (ParameterizedType) obj;
+ final Type thatRawType = that.getRawType();
+ return that.getOwnerType() == null
+ && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+ && Arrays.equals(arguments, that.getActualTypeArguments());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append(((Class<?>) rawType).getSimpleName());
+ final Type[] actualTypes = getActualTypeArguments();
+ if (actualTypes.length > 0) {
+ buffer.append("<");
+ int length = actualTypes.length;
+ for (int i = 0; i < length; i++) {
+ buffer.append(actualTypes[i].toString());
+ if (i != actualTypes.length - 1) {
+ buffer.append(",");
+ }
+ }
+
+ buffer.append(">");
+ }
+ return buffer.toString();
+ }
}
}
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
index d7ff83d..73baa18 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java
@@ -45,8 +45,10 @@ public class TypesTest {
assertTypeParameters(UUIDToStringConverter.class, Converter.class, UUID.class, String.class);
}
- private static void assertTypeParameters(Class<?> klass, Class<?> parameterizedClass, Type... types) {
- ParameterizedType parameterizedType = Types.findParameterizedType(klass, parameterizedClass);
+ private static void assertTypeParameters(final Class<?> klass,
+ final Class<?> parameterizedClass,
+ final Type... types) {
+ ParameterizedType parameterizedType = new Types().findParameterizedType(klass, parameterizedClass);
Assert.assertNotNull(parameterizedType);
Assert.assertEquals(parameterizedType.getRawType(), parameterizedClass);
Assert.assertArrayEquals(types, parameterizedType.getActualTypeArguments());
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 7189a5d..70e3a45 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -253,6 +253,7 @@ public class JohnzonBuilder implements JsonbBuilder {
getBeanManager(); // force detection
+ final Types types = new Types();
builder.setReadAttributeBeforeWrite(
config.getProperty("johnzon.readAttributeBeforeWrite").map(Boolean.class::cast).orElse(false));
builder.setAutoAdjustStringBuffers(
@@ -275,7 +276,7 @@ public class JohnzonBuilder implements JsonbBuilder {
config.getProperty(JsonbConfig.SERIALIZERS).map(JsonbSerializer[].class::cast).ifPresent(serializers -> {
Stream.of(serializers).forEach(s -> {
- final ParameterizedType pt = Types.findParameterizedType(s.getClass(), JsonbSerializer.class);
+ final ParameterizedType pt = types.findParameterizedType(s.getClass(), JsonbSerializer.class);
final Type[] args = pt.getActualTypeArguments();
// TODO: support PT in ObjectConverter (list)
if (args.length != 1 || !Class.class.isInstance(args[0])) {
@@ -290,7 +291,7 @@ public class JohnzonBuilder implements JsonbBuilder {
});
config.getProperty(JsonbConfig.DESERIALIZERS).map(JsonbDeserializer[].class::cast).ifPresent(deserializers -> {
Stream.of(deserializers).forEach(d -> {
- final ParameterizedType pt = Types.findParameterizedType(d.getClass(), JsonbDeserializer.class);
+ final ParameterizedType pt = types.findParameterizedType(d.getClass(), JsonbDeserializer.class);
final Type[] args = pt.getActualTypeArguments();
if (args.length != 1 || !Class.class.isInstance(args[0])) {
throw new IllegalArgumentException("We only support deserializer on Class for now");
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index c0fcd33..f9d0ce2 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -18,52 +18,11 @@
*/
package org.apache.johnzon.jsonb;
-import org.apache.johnzon.core.Types;
-import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter;
-import org.apache.johnzon.jsonb.converter.JsonbDateConverter;
-import org.apache.johnzon.jsonb.converter.JsonbLocalDateConverter;
-import org.apache.johnzon.jsonb.converter.JsonbLocalDateTimeConverter;
-import org.apache.johnzon.jsonb.converter.JsonbNumberConverter;
-import org.apache.johnzon.jsonb.converter.JsonbValueConverter;
-import org.apache.johnzon.jsonb.converter.JsonbZonedDateTimeConverter;
-import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
-import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
-import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
-import org.apache.johnzon.mapper.Adapter;
-import org.apache.johnzon.mapper.Converter;
-import org.apache.johnzon.mapper.JohnzonAny;
-import org.apache.johnzon.mapper.JohnzonConverter;
-import org.apache.johnzon.mapper.MapperConverter;
-import org.apache.johnzon.mapper.ObjectConverter;
-import org.apache.johnzon.mapper.TypeAwareAdapter;
-import org.apache.johnzon.mapper.access.AccessMode;
-import org.apache.johnzon.mapper.access.BaseAccessMode;
-import org.apache.johnzon.mapper.access.FieldAccessMode;
-import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
-import org.apache.johnzon.mapper.access.Meta;
-import org.apache.johnzon.mapper.access.MethodAccessMode;
-import org.apache.johnzon.mapper.converter.ReversedAdapter;
-import org.apache.johnzon.mapper.internal.AdapterKey;
-import org.apache.johnzon.mapper.internal.ConverterAdapter;
-
-import javax.json.bind.JsonbException;
-import javax.json.bind.adapter.JsonbAdapter;
-import javax.json.bind.annotation.JsonbCreator;
-import javax.json.bind.annotation.JsonbDateFormat;
-import javax.json.bind.annotation.JsonbNillable;
-import javax.json.bind.annotation.JsonbNumberFormat;
-import javax.json.bind.annotation.JsonbProperty;
-import javax.json.bind.annotation.JsonbPropertyOrder;
-import javax.json.bind.annotation.JsonbTransient;
-import javax.json.bind.annotation.JsonbTypeAdapter;
-import javax.json.bind.annotation.JsonbTypeDeserializer;
-import javax.json.bind.annotation.JsonbTypeSerializer;
-import javax.json.bind.config.PropertyNamingStrategy;
-import javax.json.bind.config.PropertyOrderStrategy;
-import javax.json.bind.config.PropertyVisibilityStrategy;
-import javax.json.bind.serializer.JsonbDeserializer;
-import javax.json.bind.serializer.JsonbSerializer;
-import javax.json.stream.JsonParserFactory;
+import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.apache.johnzon.mapper.reflection.Converters.matches;
import java.io.Closeable;
import java.io.IOException;
@@ -90,18 +49,66 @@ import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
+import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Collector;
import java.util.stream.Stream;
-import static java.util.Arrays.asList;
-import static java.util.Optional.ofNullable;
-import static org.apache.johnzon.mapper.reflection.Converters.matches;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.bind.adapter.JsonbAdapter;
+import javax.json.bind.annotation.JsonbCreator;
+import javax.json.bind.annotation.JsonbDateFormat;
+import javax.json.bind.annotation.JsonbNillable;
+import javax.json.bind.annotation.JsonbNumberFormat;
+import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.annotation.JsonbPropertyOrder;
+import javax.json.bind.annotation.JsonbTransient;
+import javax.json.bind.annotation.JsonbTypeAdapter;
+import javax.json.bind.annotation.JsonbTypeDeserializer;
+import javax.json.bind.annotation.JsonbTypeSerializer;
+import javax.json.bind.config.PropertyNamingStrategy;
+import javax.json.bind.config.PropertyOrderStrategy;
+import javax.json.bind.config.PropertyVisibilityStrategy;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.stream.JsonParserFactory;
+
+import org.apache.johnzon.core.Types;
+import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter;
+import org.apache.johnzon.jsonb.converter.JsonbDateConverter;
+import org.apache.johnzon.jsonb.converter.JsonbLocalDateConverter;
+import org.apache.johnzon.jsonb.converter.JsonbLocalDateTimeConverter;
+import org.apache.johnzon.jsonb.converter.JsonbNumberConverter;
+import org.apache.johnzon.jsonb.converter.JsonbValueConverter;
+import org.apache.johnzon.jsonb.converter.JsonbZonedDateTimeConverter;
+import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
+import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
+import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
+import org.apache.johnzon.mapper.Adapter;
+import org.apache.johnzon.mapper.Converter;
+import org.apache.johnzon.mapper.JohnzonAny;
+import org.apache.johnzon.mapper.JohnzonConverter;
+import org.apache.johnzon.mapper.MapperConverter;
+import org.apache.johnzon.mapper.MappingParser;
+import org.apache.johnzon.mapper.ObjectConverter;
+import org.apache.johnzon.mapper.TypeAwareAdapter;
+import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.access.BaseAccessMode;
+import org.apache.johnzon.mapper.access.FieldAccessMode;
+import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
+import org.apache.johnzon.mapper.access.Meta;
+import org.apache.johnzon.mapper.access.MethodAccessMode;
+import org.apache.johnzon.mapper.converter.ReversedAdapter;
+import org.apache.johnzon.mapper.internal.AdapterKey;
+import org.apache.johnzon.mapper.internal.ConverterAdapter;
public class JsonbAccessMode implements AccessMode, Closeable {
private final PropertyNamingStrategy naming;
@@ -126,6 +133,7 @@ public class JsonbAccessMode implements AccessMode, Closeable {
}
};
private boolean failOnMissingCreatorValues;
+ private final Types types = new Types();
public JsonbAccessMode(final PropertyNamingStrategy propertyNamingStrategy, final String orderValue,
final PropertyVisibilityStrategy visibilityStrategy, final boolean caseSensitive,
@@ -208,7 +216,8 @@ public class JsonbAccessMode implements AccessMode, Closeable {
try {
if (adapter != null) {
- final Adapter converter = toConverter(parameter.getType(), adapter, dateFormat, numberFormat);
+ final Adapter converter = toConverter(
+ this.types, parameter.getType(), adapter, dateFormat, numberFormat);
if (matches(parameter.getParameterizedType(), converter)) {
converters[i] = converter;
itemConverters[i] = null;
@@ -331,13 +340,13 @@ public class JsonbAccessMode implements AccessMode, Closeable {
}
}
- private Adapter<?, ?> toConverter(final Type type,
+ private Adapter<?, ?> toConverter(final Types types, final Type type,
final JsonbTypeAdapter adapter, final JsonbDateFormat dateFormat,
- final JsonbNumberFormat numberFormat) throws InstantiationException, IllegalAccessException {
+ final JsonbNumberFormat numberFormat) {
final Adapter converter;
if (adapter != null) {
final Class<? extends JsonbAdapter> value = adapter.value();
- final ParameterizedType pt = Types.findParameterizedType(value, JsonbAdapter.class);
+ final ParameterizedType pt = types.findParameterizedType(value, JsonbAdapter.class);
final JohnzonAdapterFactory.Instance<? extends JsonbAdapter> instance = newInstance(value);
toRelease.add(instance);
final Type[] actualTypeArguments = pt.getActualTypeArguments();
@@ -411,7 +420,7 @@ public class JsonbAccessMode implements AccessMode, Closeable {
reader = finalReader::read;
}
- final WriterConverters writerConverters = new WriterConverters(initialReader);
+ final WriterConverters writerConverters = new WriterConverters(initialReader, types);
final JsonbProperty property = initialReader.getAnnotation(JsonbProperty.class);
final JsonbNillable nillable = initialReader.getClassOrPackageAnnotation(JsonbNillable.class);
final boolean isNillable = nillable != null || (property != null && property.nillable());
@@ -604,7 +613,7 @@ public class JsonbAccessMode implements AccessMode, Closeable {
private ParsingCacheEntry getClassEntry(final Class<?> clazz) {
ParsingCacheEntry cache = parsingCache.get(clazz);
if (cache == null) {
- cache = new ParsingCacheEntry(new ClassDecoratedType(clazz));
+ cache = new ParsingCacheEntry(new ClassDecoratedType(clazz), types);
parsingCache.putIfAbsent(clazz, cache);
}
return cache;
@@ -719,21 +728,60 @@ public class JsonbAccessMode implements AccessMode, Closeable {
final JohnzonConverter johnzonConverter = annotationHolder.getAnnotation(JohnzonConverter.class);
validateAnnotations(annotationHolder, adapter, dateFormat, numberFormat, johnzonConverter);
- try {
- converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
- defaultConverters.get(new AdapterKey(annotationHolder.getType(), String.class)) :
- toConverter(annotationHolder.getType(), adapter, dateFormat, numberFormat);
- } catch (final InstantiationException | IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- }
+ converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
+ defaultConverters.get(new AdapterKey(annotationHolder.getType(), String.class)) :
+ toConverter(types, annotationHolder.getType(), adapter, dateFormat, numberFormat);
if (deserializer != null) {
final Class<? extends JsonbDeserializer> value = deserializer.value();
- final ParameterizedType pt = Types.findParameterizedType(value, JsonbDeserializer.class);
final JohnzonAdapterFactory.Instance<? extends JsonbDeserializer> instance = newInstance(value);
+ final ParameterizedType pt = types.findParameterizedType(value, JsonbDeserializer.class);
+ final Class<?> mappedType = types.findParamType(pt, JsonbDeserializer.class);
toRelease.add(instance);
- reader = (jsonObject, targetType, parser) ->
- instance.getValue().deserialize(JsonValueParserAdapter.createFor(jsonObject, parserFactory), new JohnzonDeserializationContext(parser), targetType);
+ reader = new ObjectConverter.Reader() {
+ private final ConcurrentMap<Type, BiFunction<JsonValue, MappingParser, Object>> impl =
+ new ConcurrentHashMap<>();
+
+ @Override
+ public Object fromJson(final JsonValue value,
+ final Type targetType,
+ final MappingParser parser) {
+ final JsonbDeserializer jsonbDeserializer = instance.getValue();
+ if (targetType == mappedType) { // fast test and matches most cases
+ return mapItem(value, targetType, parser, jsonbDeserializer);
+ }
+
+ BiFunction<JsonValue, MappingParser, Object> fn = impl.get(targetType);
+ if (fn == null) {
+ if (value.getValueType() == JsonValue.ValueType.ARRAY) {
+ if (ParameterizedType.class.isInstance(targetType)) {
+ final ParameterizedType parameterizedType = ParameterizedType.class.cast(targetType);
+ final Class<?> paramType = types.findParamType(parameterizedType, Collection.class);
+ if (paramType != null && (mappedType == null /*Object*/ || mappedType.isAssignableFrom(paramType))) {
+ final Collector<Object, ?, ? extends Collection<Object>> collector =
+ Set.class.isAssignableFrom(
+ types.asClass(parameterizedType.getRawType())) ? toSet() : toList();
+ fn = (json, mp) -> json.asJsonArray().stream()
+ .map(i -> mapItem(i, paramType, mp, jsonbDeserializer))
+ .collect(collector);
+ }
+ }
+ }
+ if (fn == null) {
+ fn = (json, mp) -> mapItem(json, targetType, mp, jsonbDeserializer);
+ }
+ impl.putIfAbsent(targetType, fn);
+ }
+ return fn.apply(value, parser);
+ }
+
+ private Object mapItem(final JsonValue jsonValue, final Type targetType,
+ final MappingParser parser, final JsonbDeserializer jsonbDeserializer) {
+ return jsonbDeserializer.deserialize(
+ JsonValueParserAdapter.createFor(jsonValue, parserFactory),
+ new JohnzonDeserializationContext(parser), targetType);
+ }
+ };
} else if (johnzonConverter != null) {
try {
MapperConverter mapperConverter = johnzonConverter.value().newInstance();
@@ -753,7 +801,7 @@ public class JsonbAccessMode implements AccessMode, Closeable {
private Adapter<?, ?> converter;
private ObjectConverter.Writer writer;
- WriterConverters(final DecoratedType initialReader) {
+ WriterConverters(final DecoratedType initialReader, final Types types) {
final JsonbTypeSerializer serializer = initialReader.getAnnotation(JsonbTypeSerializer.class);
final JsonbTypeAdapter adapter = initialReader.getAnnotation(JsonbTypeAdapter.class);
final JsonbDateFormat dateFormat = initialReader.getAnnotation(JsonbDateFormat.class);
@@ -761,17 +809,12 @@ public class JsonbAccessMode implements AccessMode, Closeable {
final JohnzonConverter johnzonConverter = initialReader.getAnnotation(JohnzonConverter.class);
validateAnnotations(initialReader, adapter, dateFormat, numberFormat, johnzonConverter);
- try {
- converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
- defaultConverters.get(new AdapterKey(initialReader.getType(), String.class)) :
- toConverter(initialReader.getType(), adapter, dateFormat, numberFormat);
- } catch (final InstantiationException | IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- }
+ converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
+ defaultConverters.get(new AdapterKey(initialReader.getType(), String.class)) :
+ toConverter(types, initialReader.getType(), adapter, dateFormat, numberFormat);
if (serializer != null) {
final Class<? extends JsonbSerializer> value = serializer.value();
- final ParameterizedType pt = Types.findParameterizedType(value, JsonbSerializer.class);
final JohnzonAdapterFactory.Instance<? extends JsonbSerializer> instance = newInstance(value);
toRelease.add(instance);
writer = (instance1, jsonbGenerator) ->
@@ -828,9 +871,9 @@ public class JsonbAccessMode implements AccessMode, Closeable {
private final ReaderConverters readers;
private final WriterConverters writers;
- ParsingCacheEntry(final DecoratedType type) {
+ ParsingCacheEntry(final DecoratedType type, final Types types) {
readers = new ReaderConverters(type);
- writers = new WriterConverters(type);
+ writers = new WriterConverters(type, types);
}
}
}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SeriaizersRoundTripTest.java
similarity index 64%
rename from johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java
rename to johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SeriaizersRoundTripTest.java
index f813cef..c87ab3a 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SeriaizersRoundTripTest.java
@@ -18,8 +18,10 @@
*/
package org.apache.johnzon.jsonb;
-import java.io.StringWriter;
+import static org.junit.Assert.assertEquals;
+
import java.lang.reflect.Type;
+import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -40,7 +42,7 @@ import javax.json.stream.JsonParser;
import org.junit.Test;
-public class MoreTests {
+public class SeriaizersRoundTripTest {
public enum Color {
@@ -66,6 +68,28 @@ public class MoreTests {
public static Option of(boolean value) {
return value ? YES : NO;
}
+
+ @Override
+ public String toString() {
+ return "Option{value=" + value + '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Option option = (Option) o;
+ return value == option.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
}
public static class VATNumber {
@@ -79,6 +103,28 @@ public class MoreTests {
public long getValue() {
return value;
}
+
+ @Override
+ public String toString() {
+ return "VATNumber{value=" + value + '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ VATNumber vatNumber = (VATNumber) o;
+ return value == vatNumber.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
}
public interface Composite<T, X> extends JsonbSerializer<T>, JsonbDeserializer<T>, JsonbAdapter<T, X> {}
@@ -171,36 +217,66 @@ public class MoreTests {
@JsonbTypeSerializer(UUIDComposite.class)
@JsonbTypeDeserializer(UUIDComposite.class)
- public UUID uuid = UUID.randomUUID();
+ public UUID uuid;
@JsonbTypeAdapter(UUIDComposite.class)
- public UUID uuid2 = UUID.randomUUID();
+ public UUID uuid2;
@JsonbTypeSerializer(OptionDeSer.class)
@JsonbTypeDeserializer(OptionDeSer.class)
- public Option option = Option.YES;
+ public Option option;
@JsonbTypeSerializer(VATDeSer.class)
@JsonbTypeDeserializer(VATDeSer.class)
- public VATNumber vatNumber = new VATNumber(42);
+ public VATNumber vatNumber;
-// @JsonbTypeSerializer(CharsDeSer.class)
-// @JsonbTypeDeserializer(CharsDeSer.class)
- // TODO Not working as @JsonbTypeSerializer seems to be ignored ("hello world" instead of ["h", "e"...])
- public String hello = "hello world";
+ @JsonbTypeSerializer(CharsDeSer.class)
+ @JsonbTypeDeserializer(CharsDeSer.class)
+ public String hello;
-// @JsonbTypeSerializer(ColorDeSer.class)
-// @JsonbTypeDeserializer(ColorDeSer.class)
- // TODO Not working as @JsonbTypeSerializer seems to be ignored ("GREEN" instead of "G")
- public Color color = Color.GREEN;
-
+ @JsonbTypeSerializer(ColorDeSer.class)
+ @JsonbTypeDeserializer(ColorDeSer.class)
+ public Color color;
+
+ @Override
+ public String toString() {
+ return "Wrapper{uuid=" + uuid + ", uuid2=" + uuid2 + ", option=" + option +
+ ", vatNumber=" + vatNumber + ", hello='" + hello + '\'' + ", color=" + color + '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final Wrapper wrapper = (Wrapper) o;
+ return Objects.equals(uuid, wrapper.uuid) && Objects.equals(uuid2, wrapper.uuid2) && Objects.equals(option,
+ wrapper.option) && Objects.equals(vatNumber, wrapper.vatNumber) && Objects.equals(hello,
+ wrapper.hello) && color == wrapper.color;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, uuid2, option, vatNumber, hello, color);
+ }
}
@Test
- public void testIt() {
- Jsonb jsonb = JsonbBuilder.create();
- StringWriter w = new StringWriter();
- jsonb.toJson(new Wrapper(), w);
- jsonb.fromJson(w.toString(), Wrapper.class);
+ public void roundTrip() throws Exception {
+ final Wrapper original = new Wrapper();
+ original.uuid = UUID.randomUUID();
+ original.uuid2 = UUID.randomUUID();
+ original.option = Option.YES;
+ original.vatNumber = new VATNumber(42);
+ original.hello = "hello world";
+ original.color = Color.GREEN;
+
+ try (final Jsonb jsonb = JsonbBuilder.create()) {
+ final Wrapper deserialized = jsonb.fromJson(jsonb.toJson(original), Wrapper.class);
+ assertEquals(original, deserialized);
+ }
}
}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
index fff01e6..f8598de 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
@@ -68,7 +68,8 @@ public class DynamicMappingGenerator implements MappingGenerator {
}
private enum WritingState {
- NONE, WROTE_START_OBJECT, PRIMITIVE
+ NONE, WROTE_START_OBJECT,
+ DONT_WRITE_END
}
private static class InObjectOrPrimitiveJsonGenerator implements JsonGenerator {
@@ -105,6 +106,10 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator writeStartArray() {
+ if (keyIfNoObject != null && state == WritingState.NONE) {
+ state = WritingState.DONT_WRITE_END; // skip writeEnd since the impl will do it
+ return delegate.writeStartArray(keyIfNoObject);
+ }
return delegate.writeStartArray();
}
@@ -182,7 +187,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final JsonValue value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -191,7 +196,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final String value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -200,7 +205,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final BigDecimal value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -209,7 +214,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final BigInteger value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -218,7 +223,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final int value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -227,7 +232,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final long value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -236,7 +241,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(final double value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -245,7 +250,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator write(boolean value) {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.write(keyIfNoObject, value);
}
return delegate.write(value);
@@ -254,7 +259,7 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator writeNull() {
if (isWritingPrimitive()) {
- state = WritingState.PRIMITIVE;
+ state = WritingState.DONT_WRITE_END;
return delegate.writeNull(keyIfNoObject);
}
return delegate.writeNull();
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
index 1f5a0a3..b7d1629 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
@@ -371,8 +371,23 @@ public class MappingGeneratorImpl implements MappingGenerator {
writeMapBody((Map<?, ?>) value, itemConverter);
generator.writeEnd();
} else if (primitive || (dynamic && Mappings.isPrimitive(type))) {
- writePrimitives(key, type, value, generator);
+ if (objectConverter != null) {
+ final DynamicMappingGenerator dynamicMappingGenerator = new DynamicMappingGenerator(this,
+ () -> this.generator.writeStartObject(key), this.generator::writeEnd, key);
+ objectConverter.writeJson(value, dynamicMappingGenerator);
+ dynamicMappingGenerator.flushIfNeeded();
+ } else {
+ writePrimitives(key, type, value, generator);
+ }
} else {
+ if (objectConverter != null) {
+ final DynamicMappingGenerator dynamicMappingGenerator = new DynamicMappingGenerator(this,
+ () -> this.generator.writeStartObject(key), this.generator::writeEnd, key);
+ objectConverter.writeJson(value, dynamicMappingGenerator);
+ dynamicMappingGenerator.flushIfNeeded();
+ return;
+ }
+
final Adapter converter = config.findAdapter(type);
if (converter != null) {
final Object adapted = doConvertFrom(value, converter);
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
index 198c573..63549a4 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
@@ -335,7 +335,9 @@ public class MappingParserImpl implements MappingParser {
}
}
}
- final Object convertedValue = toValue(existingInstance, jsonValue, value.converter, value.itemConverter, value.paramType, value.objectConverter,
+ final Object convertedValue = toValue(
+ existingInstance, jsonValue, value.converter, value.itemConverter,
+ value.paramType, value.objectConverter,
new JsonPointerTracker(jsonPointer, setter.getKey()), inType);
if (convertedValue != null) {
setterMethod.write(t, convertedValue);
@@ -727,12 +729,7 @@ public class MappingParserImpl implements MappingParser {
final JsonPointerTracker jsonPointer, final Type rootType) {
if (objectConverter != null) {
-
- if (jsonValue instanceof JsonArray) {
- return buildArray(type, jsonValue.asJsonArray(), itemConverter, objectConverter, jsonPointer, rootType);
- } else {
- return objectConverter.fromJson(jsonValue, type, this);
- }
+ return objectConverter.fromJson(jsonValue, type, this);
}
try {