Repository: johnzon Updated Branches: refs/heads/master fb5d8b7a9 -> eb93b91a5
JAX-RS MessageWriter/MessagerReader with Priority (so that user can define and provide his own) JsonbConfig injectable via Jax-RS API Better support for generics types Support for JsonbDeserializers/JsonbSerializers defined in interfaces or abstract classes Support for default deserialization from string to enum Fix to dateformatting (date format was not properly used in deserialization) Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/028a1644 Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/028a1644 Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/028a1644 Branch: refs/heads/master Commit: 028a1644aac2e565fbed007ba85e8f0bede9061f Parents: fb5d8b7 Author: amoscatelli <dragonide001> Authored: Mon Mar 26 16:02:31 2018 +0200 Committer: amoscatelli <dragonide001> Committed: Mon Mar 26 16:02:31 2018 +0200 ---------------------------------------------------------------------- johnzon-jsonb/pom.xml | 15 ++ .../jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java | 99 ++++------- .../apache/johnzon/jsonb/JohnzonBuilder.java | 167 +++++++++---------- .../johnzon/mapper/MappingParserImpl.java | 90 ++++++---- 4 files changed, 181 insertions(+), 190 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/028a1644/johnzon-jsonb/pom.xml ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/pom.xml b/johnzon-jsonb/pom.xml index d63250e..30585b0 100644 --- a/johnzon-jsonb/pom.xml +++ b/johnzon-jsonb/pom.xml @@ -35,6 +35,14 @@ </properties> <dependencies> + + <!-- FOR PRIORITY ANNOTATION NOT FOUND IN GERONIMO (??) --> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>javax.annotation-api</artifactId> + <version>1.3.2</version> + </dependency> + <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-jaxrs_2.0_spec</artifactId> @@ -62,6 +70,13 @@ <version>${project.version}</version> </dependency> + <!-- FOR GENERICS DISCOVERY --> + <dependency> + <groupId>com.googlecode.gentyref</groupId> + <artifactId>gentyref</artifactId> + <version>1.2.0</version> + </dependency> + <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-rs-client</artifactId> http://git-wip-us.apache.org/repos/asf/johnzon/blob/028a1644/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java index 49cd334..f5b2de8 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java @@ -21,7 +21,6 @@ package org.apache.johnzon.jaxrs.jsonb.jaxrs; import javax.json.JsonStructure; import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; -import javax.json.bind.JsonbConfig; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; @@ -36,22 +35,29 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; -import java.io.StringReader; import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; -import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Providers; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; // here while we dont compile in java 8 jaxrs module, when migrated we'll merge it with IgnorableTypes hierarchy at least @Provider -@Produces("application/json") -@Consumes("application/json") +@Priority(value = Priorities.USER-100) +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> { + protected final Collection<String> ignores; protected final AtomicReference<Jsonb> delegate = new AtomicReference<>(); - protected final JsonbConfig config = new JsonbConfig(); + + @Context + private Providers providers; public JsonbJaxrsProvider() { this(null); @@ -61,60 +67,11 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR this.ignores = ignores; } - protected Jsonb createJsonb() { - return JsonbBuilder.create(config); - } - private boolean isIgnored(final Class<?> type) { return ignores != null && ignores.contains(type.getName()); } - // config - main containers support the configuration of providers this way - public void setFailOnUnknownProperties(final boolean active) { - config.setProperty("johnzon.fail-on-unknown-properties", active); - } - - public void setOtherProperties(final String others) { - final Properties properties = new Properties() {{ - try { - load(new StringReader(others)); - } catch (final IOException e) { - throw new IllegalArgumentException(e); - } - }}; - properties.stringPropertyNames().forEach(k -> config.setProperty(k, properties.getProperty(k))); - } - - public void setIJson(final boolean active) { - config.withStrictIJSON(active); - } - - public void setEncoding(final String encoding) { - config.withEncoding(encoding); - } - - public void setBinaryDataStrategy(final String binaryDataStrategy) { - config.withBinaryDataStrategy(binaryDataStrategy); - } - - public void setPropertyNamingStrategy(final String propertyNamingStrategy) { - config.withPropertyNamingStrategy(propertyNamingStrategy); - } - - public void setPropertyOrderStrategy(final String propertyOrderStrategy) { - config.withPropertyOrderStrategy(propertyOrderStrategy); - } - - public void setNullValues(final boolean nulls) { - config.withNullValues(nulls); - } - - public void setPretty(final boolean pretty) { - config.withFormatting(pretty); - } - // actual impl - @Override public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return !isIgnored(type) @@ -144,26 +101,30 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR @Override public T readFrom(final Class<T> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { - return delegate().fromJson(entityStream, genericType); + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { + return delegate(type).fromJson(entityStream, genericType); } @Override public void writeTo(final T t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { - delegate().toJson(t, entityStream); + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { + delegate(type).toJson(t, entityStream); } - private Jsonb delegate() { - Jsonb jsonb = delegate.get(); - if (jsonb == null) { - final Jsonb newJsonb = createJsonb(); - if (delegate.compareAndSet(null, newJsonb)) { - jsonb = newJsonb; - } else { - jsonb = delegate.get(); - } + protected Jsonb getJsonb(Class<?> type) { + ContextResolver<Jsonb> contextResolver = providers.getContextResolver(Jsonb.class, MediaType.APPLICATION_JSON_TYPE); + if (contextResolver != null) { + return contextResolver.getContext(type); + } else { + return JsonbBuilder.create(); } - return jsonb; } + + private Jsonb delegate(Class<?> type) { + if (delegate.get() == null) { + delegate.compareAndSet(null, getJsonb(type)); + } + return delegate.get(); + } + } http://git-wip-us.apache.org/repos/asf/johnzon/blob/028a1644/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java ---------------------------------------------------------------------- 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 1ae554a..eb834b2 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 @@ -18,6 +18,7 @@ */ package org.apache.johnzon.jsonb; +import com.googlecode.gentyref.GenericTypeReflector; import org.apache.johnzon.core.AbstractJsonFactory; import org.apache.johnzon.core.JsonGeneratorFactoryImpl; import org.apache.johnzon.core.JsonParserFactoryImpl; @@ -96,6 +97,7 @@ import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY; import static javax.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL; public class JohnzonBuilder implements JsonbBuilder { + private static final Object NO_BM = new Object(); private final MapperBuilder builder = new MapperBuilder(); @@ -141,52 +143,52 @@ public class JohnzonBuilder implements JsonbBuilder { final String orderValue = config.getProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY).map(String::valueOf).orElse(LEXICOGRAPHICAL); final PropertyVisibilityStrategy visibilityStrategy = config.getProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY) .map(PropertyVisibilityStrategy.class::cast).orElse(new PropertyVisibilityStrategy() { - private final ConcurrentMap<Class<?>, PropertyVisibilityStrategy> strategies = new ConcurrentHashMap<>(); + private final ConcurrentMap<Class<?>, PropertyVisibilityStrategy> strategies = new ConcurrentHashMap<>(); - @Override - public boolean isVisible(final Field field) { - if (field.getAnnotation(JsonbProperty.class) != null) { - return true; - } - final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(field.getDeclaringClass(), this::visibilityStrategy); - return strategy == this ? Modifier.isPublic(field.getModifiers()) : strategy.isVisible(field); - } + @Override + public boolean isVisible(final Field field) { + if (field.getAnnotation(JsonbProperty.class) != null) { + return true; + } + final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(field.getDeclaringClass(), this::visibilityStrategy); + return strategy == this ? Modifier.isPublic(field.getModifiers()) : strategy.isVisible(field); + } - @Override - public boolean isVisible(final Method method) { - final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(method.getDeclaringClass(), this::visibilityStrategy); - return strategy == this ? Modifier.isPublic(method.getModifiers()) : strategy.isVisible(method); - } + @Override + public boolean isVisible(final Method method) { + final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(method.getDeclaringClass(), this::visibilityStrategy); + return strategy == this ? Modifier.isPublic(method.getModifiers()) : strategy.isVisible(method); + } - private PropertyVisibilityStrategy visibilityStrategy(final Class<?> type) { // can be cached - JsonbVisibility visibility = type.getAnnotation(JsonbVisibility.class); - if (visibility != null) { - try { - return visibility.value().newInstance(); - } catch (final InstantiationException | IllegalAccessException e) { - throw new IllegalArgumentException(e); - } - } - Package p = type.getPackage(); - while (p != null) { - visibility = p.getAnnotation(JsonbVisibility.class); - if (visibility != null) { - try { - return visibility.value().newInstance(); - } catch (final InstantiationException | IllegalAccessException e) { - throw new IllegalArgumentException(e); - } - } - final String name = p.getName(); - final int end = name.lastIndexOf('.'); - if (end < 0) { - break; - } - p = Package.getPackage(name.substring(0, end)); + private PropertyVisibilityStrategy visibilityStrategy(final Class<?> type) { // can be cached + JsonbVisibility visibility = type.getAnnotation(JsonbVisibility.class); + if (visibility != null) { + try { + return visibility.value().newInstance(); + } catch (final InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException(e); + } + } + Package p = type.getPackage(); + while (p != null) { + visibility = p.getAnnotation(JsonbVisibility.class); + if (visibility != null) { + try { + return visibility.value().newInstance(); + } catch (final InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException(e); } - return this; } - }); + final String name = p.getName(); + final int end = name.lastIndexOf('.'); + if (end < 0) { + break; + } + p = Package.getPackage(name.substring(0, end)); + } + return this; + } + }); config.getProperty("johnzon.attributeOrder").ifPresent(comp -> builder.setAttributeOrder(Comparator.class.cast(comp))); config.getProperty("johnzon.enforceQuoteString") @@ -227,16 +229,15 @@ public class JohnzonBuilder implements JsonbBuilder { final AccessMode accessMode = config.getProperty("johnzon.accessMode") .map(this::toAccessMode) .orElseGet(() -> new JsonbAccessMode( - propertyNamingStrategy, orderValue, visibilityStrategy, - !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE), - defaultConverters, - factory, parserFactoryProvider, - config.getProperty("johnzon.accessModeDelegate") - .map(this::toAccessMode) - .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false)))); + propertyNamingStrategy, orderValue, visibilityStrategy, + !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE), + defaultConverters, + factory, parserFactoryProvider, + config.getProperty("johnzon.accessModeDelegate") + .map(this::toAccessMode) + .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false)))); builder.setAccessMode(accessMode); - // user adapters config.getProperty(JsonbConfig.ADAPTERS).ifPresent(adapters -> Stream.of(JsonbAdapter[].class.cast(adapters)).forEach(adapter -> { final ParameterizedType pt = ParameterizedType.class.cast( @@ -306,35 +307,34 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty(JsonbConfig.SERIALIZERS).map(JsonbSerializer[].class::cast).ifPresent(serializers -> { Stream.of(serializers).forEach(s -> { - final ParameterizedType pt = findPT(s, JsonbSerializer.class); - if (pt == null) { - throw new IllegalArgumentException(s + " doesn't implement JsonbSerializer"); - } - final Type[] args = pt.getActualTypeArguments(); - // TODO: support PT in ObjectConverter (list) - if (args.length != 1 || !Class.class.isInstance(args[0])) { - throw new IllegalArgumentException("We only support serializer on Class for now"); + Type typeParameter = GenericTypeReflector.getTypeParameter(s.getClass(), JsonbDeserializer.class.getTypeParameters()[0]); + if (typeParameter instanceof Class) { + builder.addObjectConverter( + Class.class.cast(typeParameter), + (ObjectConverter.Writer) (instance, jsonbGenerator) + -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator))); + } else if (typeParameter instanceof ParameterizedType) { + builder.addObjectConverter( + Class.class.cast(ParameterizedType.class.cast(typeParameter).getRawType()), + (ObjectConverter.Writer) (instance, jsonbGenerator) + -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator))); } - builder.addObjectConverter( - Class.class.cast(args[0]), (ObjectConverter.Writer) - (instance, jsonbGenerator) -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator))); + }); }); config.getProperty(JsonbConfig.DESERIALIZERS).map(JsonbDeserializer[].class::cast).ifPresent(deserializers -> { Stream.of(deserializers).forEach(d -> { - final ParameterizedType pt = findPT(d, JsonbDeserializer.class); - if (pt == null) { - throw new IllegalArgumentException(d + " doesn't implement JsonbDeserializer"); - } - 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"); - } + Type typeParameter = GenericTypeReflector.getTypeParameter(d.getClass(), JsonbDeserializer.class.getTypeParameters()[0]); // TODO: support PT in ObjectConverter (list) - builder.addObjectConverter( - Class.class.cast(args[0]), (ObjectConverter.Reader) - (jsonObject, targetType, parser) -> d.deserialize( - parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); + if (typeParameter instanceof Class) { + builder.addObjectConverter( + Class.class.cast(typeParameter), (ObjectConverter.Reader) (jsonObject, targetType, parser) -> d.deserialize( + parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); + } else if (typeParameter instanceof ParameterizedType) { + builder.addObjectConverter( + Class.class.cast(ParameterizedType.class.cast(typeParameter).getRawType()), (ObjectConverter.Reader) (jsonObject, targetType, parser) -> d.deserialize( + parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); + } }); }); @@ -398,13 +398,6 @@ public class JohnzonBuilder implements JsonbBuilder { }; } - private ParameterizedType findPT(final Object s, final Class<?> type) { - return ParameterizedType.class.cast( - Stream.of(s.getClass().getGenericInterfaces()) - .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == type) - .findFirst().orElse(null)); - } - private Object getBeanManager() { if (beanManager == null) { try { // don't trigger CDI is not there @@ -619,7 +612,6 @@ public class JohnzonBuilder implements JsonbBuilder { })); addDateFormatConfigConverters(converters, zoneIDUTC); - converters.forEach((k, v) -> builder.addAdapter(k.getFrom(), k.getTo(), v)); return converters; } @@ -632,25 +624,16 @@ public class JohnzonBuilder implements JsonbBuilder { // Note: we try and fallback in the parsing cause we don't know if the date format provided is // for date, datetime, time - converters.put(new AdapterKey(Date.class, String.class), new ConverterAdapter<>(new Converter<Date>() { - private volatile boolean useFormatter = true; @Override public String toString(final Date instance) { - return LocalDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString(); + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); } @Override public Date fromString(final String text) { - if (useFormatter) { - try { - return Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC)); - } catch (final DateTimeParseException dpe) { - useFormatter = false; - } - } - return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC)); + return Date.from(Instant.from(formatter.parse(text))); } })); converters.put(new AdapterKey(LocalDateTime.class, String.class), new ConverterAdapter<>(new Converter<LocalDateTime>() { @@ -721,7 +704,7 @@ public class JohnzonBuilder implements JsonbBuilder { if (text.length() == 3) { // don't fail but log it Logger.getLogger(JohnzonBuilder.class.getName()).severe("Deprecated timezone: " + text); } - */ + */ } private Map<String, ?> generatorConfig() { http://git-wip-us.apache.org/repos/asf/johnzon/blob/028a1644/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java ---------------------------------------------------------------------- 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 8e74e3c..35b303a 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 @@ -18,6 +18,7 @@ */ package org.apache.johnzon.mapper; +import java.lang.reflect.AnnotatedType; import org.apache.johnzon.mapper.access.AccessMode; import org.apache.johnzon.mapper.converter.CharacterConverter; import org.apache.johnzon.mapper.converter.EnumConverter; @@ -40,10 +41,12 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.Collections; @@ -70,9 +73,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.util.Arrays.asList; +import java.util.Date; +import java.util.Optional; import static javax.json.JsonValue.ValueType.FALSE; import static javax.json.JsonValue.ValueType.NULL; import static javax.json.JsonValue.ValueType.NUMBER; +import static javax.json.JsonValue.ValueType.STRING; import static javax.json.JsonValue.ValueType.TRUE; /** @@ -93,13 +99,11 @@ public class MappingParserImpl implements MappingParser { private final JsonReader jsonReader; /** - * Used for de-referencing JsonPointers during deserialisation. - * key: JsonPointer - * value: already deserialised Object + * Used for de-referencing JsonPointers during deserialisation. key: + * JsonPointer value: already deserialised Object */ private Map<String, Object> jsonPointers; - public MappingParserImpl(MapperConfig config, Mappings mappings, JsonReader jsonReader, boolean isDeduplicateObjects) { this.config = config; this.mappings = mappings; @@ -108,7 +112,6 @@ public class MappingParserImpl implements MappingParser { reverseAdaptersRegistry = new ConcurrentHashMap<>(config.getAdapters().size()); - this.isDeduplicateObjects = isDeduplicateObjects; if (isDeduplicateObjects) { @@ -118,7 +121,6 @@ public class MappingParserImpl implements MappingParser { } } - @Override public <T> T readObject(Type targetType) { try { @@ -143,6 +145,12 @@ public class MappingParserImpl implements MappingParser { if (JsonString.class.isInstance(jsonValue) && (targetType == String.class || targetType == Object.class)) { return (T) JsonString.class.cast(jsonValue).getString(); } + if (JsonString.class.isInstance(jsonValue) && targetType instanceof Class && Class.class.cast(targetType).isEnum()) { + return (T) Enum.valueOf(Class.class.cast(targetType), JsonString.class.cast(jsonValue).getString()); + } + if (JsonString.class.isInstance(jsonValue) && targetType instanceof Class && Date.class.isAssignableFrom(Class.class.cast(targetType))) { + return (T) convertTo(config.findAdapter(targetType), jsonValue, null); + } if (JsonNumber.class.isInstance(jsonValue)) { final JsonNumber number = JsonNumber.class.cast(jsonValue); if (targetType == int.class || targetType == Integer.class) { @@ -199,7 +207,6 @@ public class MappingParserImpl implements MappingParser { throw new IllegalArgumentException("Unsupported " + jsonValue + " for type " + targetType); } - private Object buildObject(final Type inType, final JsonObject object, final boolean applyObjectConverter, JsonPointerTracker jsonPointer) { Type type = inType; if (inType == Object.class) { @@ -264,7 +271,29 @@ public class MappingParserImpl implements MappingParser { } return map; } + } else { + + // if a specific mapping has not been declared, let's try finding and using one without generics + ObjectConverter.Reader objectConverter = config.findObjectConverterReader((Class) aType.getRawType()); + + if (objectConverter != null) { + return objectConverter.fromJson(object, type, new SuppressConversionMappingParser(this, object)); + } + + } + } else if (TypeVariable.class.isInstance(type)) { + + TypeVariable vType = TypeVariable.class.cast(type); + + Optional<AnnotatedType> findFirst = Arrays.asList(vType.getAnnotatedBounds()).stream().findFirst(); + + if (findFirst.isPresent()) { + ObjectConverter.Reader objectConverter = config.findObjectConverterReader((Class) findFirst.get().getType()); + if (objectConverter != null) { + return objectConverter.fromJson(object, type, new SuppressConversionMappingParser(this, object)); + } } + } else if (Map.class == type || HashMap.class == type || LinkedHashMap.class == type) { final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(); for (final Map.Entry<String, JsonValue> value : object.entrySet()) { @@ -284,7 +313,7 @@ public class MappingParserImpl implements MappingParser { if (classMapping.adapter != null) { return classMapping.adapter.from(t); } - */ + */ if (classMapping.factory == null) { throw new MapperException(classMapping.clazz + " not instantiable"); @@ -292,9 +321,11 @@ public class MappingParserImpl implements MappingParser { if (config.isFailOnUnknown()) { if (!classMapping.setters.keySet().containsAll(object.keySet())) { - throw new MapperException("(fail on unknown properties): " + new HashSet<String>(object.keySet()) {{ - removeAll(classMapping.setters.keySet()); - }}); + throw new MapperException("(fail on unknown properties): " + new HashSet<String>(object.keySet()) { + { + removeAll(classMapping.setters.keySet()); + } + }); } } @@ -426,6 +457,9 @@ public class MappingParserImpl implements MappingParser { } } } + if (STRING.equals(valueType)) { + return converter.to(JsonString.class.cast(jsonValue).getString()); + } return converter.to(jsonValue.toString()); } @@ -459,10 +493,9 @@ public class MappingParserImpl implements MappingParser { return adapterKey; } - private Object toObject(final Object baseInstance, final JsonValue jsonValue, - final Type type, final Adapter itemConverter, final JsonPointerTracker jsonPointer, - final Type rootType) { + final Type type, final Adapter itemConverter, final JsonPointerTracker jsonPointer, + final Type rootType) { if (jsonValue == null || JsonValue.NULL.equals(jsonValue)) { return null; } @@ -506,8 +539,7 @@ public class MappingParserImpl implements MappingParser { } final boolean typedAdapter = TypeAwareAdapter.class.isInstance(itemConverter); final Object object = buildObject( - baseInstance != null ? baseInstance.getClass() : ( - typedAdapter ? TypeAwareAdapter.class.cast(itemConverter).getTo() : type), + baseInstance != null ? baseInstance.getClass() : (typedAdapter ? TypeAwareAdapter.class.cast(itemConverter).getTo() : type), JsonObject.class.cast(jsonValue), type instanceof Class, jsonPointer); return typedAdapter ? itemConverter.to(object) : object; @@ -578,8 +610,8 @@ public class MappingParserImpl implements MappingParser { } private Object buildArray(final Type type, final JsonArray jsonArray, final Adapter itemConverter, - final ObjectConverter.Reader objectConverter, - final JsonPointerTracker jsonPointer, final Type rootType) { + final ObjectConverter.Reader objectConverter, + final JsonPointerTracker jsonPointer, final Type rootType) { if (Class.class.isInstance(type)) { final Class clazz = Class.class.cast(type); if (clazz.isArray()) { @@ -603,7 +635,7 @@ public class MappingParserImpl implements MappingParser { } private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType, final Adapter itemConverter, - final JsonPointerTracker jsonPointer, final Type rootType) { + final JsonPointerTracker jsonPointer, final Type rootType) { final Object array = Array.newInstance(componentType, jsonArray.size()); int i = 0; for (final JsonValue value : jsonArray) { @@ -615,8 +647,8 @@ public class MappingParserImpl implements MappingParser { } private <T> Collection<T> mapCollection(final Mappings.CollectionMapping mapping, final JsonArray jsonArray, - final Adapter itemConverter, ObjectConverter.Reader objectConverter, - final JsonPointerTracker jsonPointer, final Type rootType) { + final Adapter itemConverter, ObjectConverter.Reader objectConverter, + final JsonPointerTracker jsonPointer, final Type rootType) { final Collection collection; if (SortedSet.class == mapping.raw || NavigableSet.class == mapping.raw || TreeSet.class == mapping.raw) { @@ -642,7 +674,7 @@ public class MappingParserImpl implements MappingParser { collection.add(JsonValue.NULL.equals(value) ? null : toValue(null, value, null, itemConverter, mapping.arg, objectConverter, - isDeduplicateObjects ? new JsonPointerTracker(jsonPointer, i) : null, rootType)); + isDeduplicateObjects ? new JsonPointerTracker(jsonPointer, i) : null, rootType)); i++; } @@ -660,7 +692,6 @@ public class MappingParserImpl implements MappingParser { return collection; } - private Object[] createParameters(final Mappings.ClassMapping mapping, final JsonObject object, JsonPointerTracker jsonPointer) { final int length = mapping.factory.getParameterTypes().length; final Object[] objects = new Object[length]; @@ -682,8 +713,8 @@ public class MappingParserImpl implements MappingParser { } private Object toValue(final Object baseInstance, final JsonValue jsonValue, final Adapter converter, - final Adapter itemConverter, final Type type, final ObjectConverter.Reader objectConverter, - final JsonPointerTracker jsonPointer, final Type rootType) { + final Adapter itemConverter, final Type type, final ObjectConverter.Reader objectConverter, + final JsonPointerTracker jsonPointer, final Type rootType) { if (objectConverter != null) { @@ -701,7 +732,6 @@ public class MappingParserImpl implements MappingParser { : convertTo(converter, jsonValue, jsonPointer); } - /** * @deprecated see MapperConfig */ @@ -743,8 +773,8 @@ public class MappingParserImpl implements MappingParser { } } if (converter == null) { - throw new MapperException("Missing a Converter for type " + aClass + " to convert the JSON String '" + - text + "' . Please register a custom converter for it."); + throw new MapperException("Missing a Converter for type " + aClass + " to convert the JSON String '" + + text + "' . Please register a custom converter for it."); } return converter.to(text); } @@ -770,9 +800,11 @@ public class MappingParserImpl implements MappingParser { /** * Internal class to suppress {@link ObjectConverter} lookup if and only if - * the {@link JsonValue} is the same refernece than the lookup was done before. + * the {@link JsonValue} is the same refernece than the lookup was done + * before. */ private static class SuppressConversionMappingParser implements MappingParser { + private final MappingParserImpl delegate; private final JsonObject suppressConversionFor;
