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 {