| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.johnzon.mapper; |
| |
| import org.apache.johnzon.mapper.access.AccessMode; |
| import org.apache.johnzon.mapper.converter.CharacterConverter; |
| import org.apache.johnzon.mapper.internal.AdapterKey; |
| import org.apache.johnzon.mapper.internal.ConverterAdapter; |
| import org.apache.johnzon.mapper.internal.JsonPointerTracker; |
| import org.apache.johnzon.mapper.number.Validator; |
| import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType; |
| |
| import javax.json.JsonArray; |
| import javax.json.JsonNumber; |
| import javax.json.JsonObject; |
| import javax.json.JsonReader; |
| import javax.json.JsonString; |
| import javax.json.JsonStructure; |
| import javax.json.JsonValue; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Base64; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.EnumMap; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.NavigableMap; |
| import java.util.NavigableSet; |
| import java.util.PriorityQueue; |
| import java.util.Queue; |
| import java.util.Set; |
| import java.util.SortedMap; |
| import java.util.SortedSet; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| import java.util.concurrent.ArrayBlockingQueue; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.function.Function; |
| import java.util.stream.DoubleStream; |
| import java.util.stream.IntStream; |
| import java.util.stream.LongStream; |
| import java.util.stream.Stream; |
| |
| import static java.util.Arrays.asList; |
| import static java.util.stream.Collectors.joining; |
| import static java.util.stream.Collectors.toList; |
| import static java.util.stream.Collectors.toMap; |
| import static javax.json.JsonValue.ValueType.ARRAY; |
| 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; |
| import static org.apache.johnzon.mapper.Mappings.getPrimitiveDefault; |
| |
| /** |
| * This class is not concurrently usable as it contains state. |
| */ |
| public class MappingParserImpl implements MappingParser { |
| |
| private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class); |
| private static final CharacterConverter CHARACTER_CONVERTER = new CharacterConverter(); // this one is particular, share the logic |
| |
| protected final ConcurrentMap<Class<?>, Method> valueOfs = new ConcurrentHashMap<Class<?>, Method>(); |
| |
| private final MapperConfig config; |
| private final Mappings mappings; |
| |
| private final JsonReader jsonReader; |
| |
| /** |
| * 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, Map<String, Object> jsonPointers) { |
| this.config = config; |
| this.mappings = mappings; |
| this.jsonReader = jsonReader; |
| } |
| |
| |
| @Override |
| public <T> T readObject(Type targetType) { |
| try { |
| return readObject(jsonReader.readValue(), targetType); |
| } catch (final NoSuchMethodError noSuchMethodError) { // jsonp 1.0 fallback - mainly for tests |
| return readObject(jsonReader.read(), targetType); |
| } |
| } |
| |
| @Override |
| public <T> T readObject(final JsonValue jsonValue, final Type targetType) { |
| return readObject(jsonValue, targetType, targetType instanceof Class || targetType instanceof ParameterizedType, null); |
| } |
| |
| public <T> T readObject(final JsonValue jsonValue, final Type targetType, final boolean applyObjectConverter, |
| final Collection<Class<?>> skippedConverters) { |
| final JsonValue.ValueType valueType = jsonValue != null ? jsonValue.getValueType() : null; |
| |
| if (JsonStructure.class == targetType || JsonObject.class == targetType || JsonValue.class == targetType) { |
| return (T) jsonValue; |
| } |
| if (JsonObject.class.isInstance(jsonValue)) { |
| return (T) buildObject( |
| targetType, JsonObject.class.cast(jsonValue), applyObjectConverter, |
| null, skippedConverters); |
| } |
| if (JsonString.class.isInstance(jsonValue)) { |
| if ((targetType == String.class || targetType == Object.class)) { |
| return (T) JsonString.class.cast(jsonValue).getString(); |
| } |
| if (targetType == Character.class || targetType == char.class) { |
| final CharSequence string = JsonString.class.cast(jsonValue).getChars(); |
| if (string.length() == 1) { |
| return (T) Character.valueOf(string.charAt(0)); |
| } |
| throw new IllegalArgumentException("Invalid Character binding"); // don't log the value (pwd case) |
| } |
| |
| final Mappings.ClassMapping classMapping = mappings.getClassMapping(targetType); |
| if (classMapping != null && classMapping.adapter != null) { |
| return (T) classMapping.adapter.to(JsonString.class.cast(jsonValue).getString()); |
| } |
| |
| final Adapter adapter = findAdapter(targetType); |
| if (adapter != null && TypeAwareAdapter.class.isInstance(adapter)) { |
| final TypeAwareAdapter typeAwareAdapter = TypeAwareAdapter.class.cast(adapter); |
| if (typeAwareAdapter.getTo() == String.class) { |
| return (T) adapter.to(JsonString.class.cast(jsonValue).getString()); |
| } |
| if (typeAwareAdapter.getTo() == JsonString.class) { |
| return (T) adapter.to(JsonString.class.cast(jsonValue)); |
| } |
| if (typeAwareAdapter.getTo() == CharSequence.class) { |
| return (T) adapter.to(JsonString.class.cast(jsonValue).getChars()); |
| } |
| } |
| } |
| if (JsonNumber.class.isInstance(jsonValue)) { |
| final JsonNumber number = JsonNumber.class.cast(jsonValue); |
| if (targetType == int.class || targetType == Integer.class) { |
| return (T) Integer.valueOf(number.intValue()); |
| } |
| if (targetType == long.class || targetType == Long.class) { |
| return (T) Long.valueOf(number.longValue()); |
| } |
| if (targetType == double.class || targetType == Double.class || targetType == Object.class) { |
| return (T) Double.valueOf(number.doubleValue()); |
| } |
| if (targetType == float.class || targetType == Float.class) { |
| return (T) Float.valueOf((float) number.doubleValue()); |
| } |
| if (targetType == byte.class || targetType == Byte.class) { |
| final int intValue = number.intValue(); |
| Validator.validateByte(intValue); |
| return (T) Byte.valueOf((byte) intValue); |
| } |
| if (targetType == short.class || targetType == Short.class) { |
| return (T) Short.valueOf((short) number.intValue()); |
| } |
| if (targetType == BigDecimal.class || Number.class == targetType) { |
| return (T) number.bigDecimalValue(); |
| } |
| if (targetType == BigInteger.class) { |
| return (T) number.bigIntegerValue(); |
| } |
| } |
| if (JsonArray.class.isInstance(jsonValue)) { |
| |
| JsonArray jsonArray = (JsonArray) jsonValue; |
| |
| if (Class.class.isInstance(targetType)) { |
| final Class<?> asClass = (Class) targetType; |
| if (asClass.isArray()) { |
| final Class componentType = asClass.getComponentType(); |
| return (T) buildArrayWithComponentType(jsonArray, componentType, config.findAdapter(componentType), |
| isDedup() ? JsonPointerTracker.ROOT : null, Object.class); |
| } |
| if (Collection.class.isAssignableFrom(asClass)) { |
| return readObject(jsonValue, new JohnzonParameterizedType(asClass, Object.class), applyObjectConverter, skippedConverters); |
| } |
| } |
| if (ParameterizedType.class.isInstance(targetType)) { |
| |
| final ParameterizedType pt = (ParameterizedType) targetType; |
| final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(pt, Object.class); |
| if (mapping == null) { |
| throw new UnsupportedOperationException("type " + targetType + " not supported"); |
| } |
| |
| final Type arg = pt.getActualTypeArguments()[0]; |
| return (T) mapCollection(mapping, jsonArray, Class.class.isInstance(arg) ? config.findAdapter(Class.class.cast(arg)) : null, |
| null, isDedup() ? JsonPointerTracker.ROOT : null, Object.class); |
| } |
| if (Object.class == targetType) { |
| return (T) new ArrayList(asList(Object[].class.cast(buildArrayWithComponentType(jsonArray, Object.class, null, |
| isDedup() ? JsonPointerTracker.ROOT : null, Object.class)))); |
| } |
| } |
| if (NULL == valueType) { |
| return null; |
| } |
| if (TRUE == valueType && (Boolean.class == targetType || boolean.class == targetType || Object.class == targetType)) { |
| return (T) Boolean.TRUE; |
| } |
| if (FALSE == valueType && (Boolean.class == targetType || boolean.class == targetType || Object.class == targetType)) { |
| return (T) Boolean.FALSE; |
| } |
| |
| final String snippet = config.getSnippet().of(jsonValue); |
| final String description = ExceptionMessages.description(valueType); |
| throw new IllegalArgumentException(targetType + " does not support " + description + ": " + snippet); |
| } |
| |
| private boolean isDedup() { |
| return jsonPointers != Collections.<String, Object>emptyMap(); |
| } |
| |
| private Object buildObject(final Type inType, final JsonObject object, final boolean applyObjectConverter, |
| final JsonPointerTracker jsonPointer, final Collection<Class<?>> skippedConverters) { |
| final Type type = inType == Object.class ? new JohnzonParameterizedType(Map.class, String.class, Object.class) : inType; |
| if (applyObjectConverter && !(type instanceof ParameterizedType)) { |
| if (!(type instanceof Class)) { |
| throw new MapperException("ObjectConverters are only supported for Classes not Types"); |
| } |
| |
| final Class clazz = (Class) type; |
| if (skippedConverters == null || !skippedConverters.contains(clazz)) { |
| ObjectConverter.Reader objectConverter = config.findObjectConverterReader(clazz); |
| if (objectConverter != null) { |
| final Collection<Class<?>> skipped = skippedConverters == null ? new ArrayList<>() : skippedConverters; |
| skipped.add(clazz); |
| return objectConverter.fromJson( |
| object, type, |
| new SuppressConversionMappingParser(this, object, skipped)); |
| } |
| } |
| } |
| |
| if (config.getDeserializationPredicate() != null && Class.class.isInstance(inType)) { |
| final Class<?> clazz = Class.class.cast(inType); |
| if (config.getDeserializationPredicate().test(clazz) && object.containsKey(config.getDiscriminator())) { |
| final String discriminator = object.getString(config.getDiscriminator()); |
| final Class<?> nestedType = config.getTypeLoader().apply(discriminator); |
| if (nestedType != null && nestedType != inType) { |
| return buildObject(nestedType, object, applyObjectConverter, jsonPointer, skippedConverters); |
| } |
| } |
| } |
| final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(type); |
| |
| if (classMapping == null) { |
| if (ParameterizedType.class.isInstance(type)) { |
| final ParameterizedType aType = ParameterizedType.class.cast(type); |
| final Type[] fieldArgTypes = aType.getActualTypeArguments(); |
| if (fieldArgTypes.length >= 2) { |
| final Class<?> raw = Class.class.cast(aType.getRawType()); |
| final Map map; |
| if (SortedMap.class.isAssignableFrom(raw) || NavigableMap.class == raw || TreeMap.class == raw) { |
| map = config.getAttributeOrder() == null ? new TreeMap() : new TreeMap(config.getAttributeOrder()); |
| } else if (ConcurrentMap.class.isAssignableFrom(raw)) { |
| map = new ConcurrentHashMap(object.size()); |
| } else if (EnumMap.class.isAssignableFrom(raw)) { |
| if (!config.isSupportEnumContainerDeserialization()) { |
| throw new MapperException("JSON-B forbids EnumMap deserialization, " + |
| "set supportEnumMapDeserialization=true to disable that arbitrary limitation"); |
| } |
| map = new EnumMap(Class.class.cast(fieldArgTypes[0])); |
| } else if (Map.class.isAssignableFrom(raw)) { |
| map = new LinkedHashMap(object.size()); // todo: configurable from config.getNewDefaultMap()? |
| } else { |
| map = null; |
| } |
| |
| if (map != null) { |
| final Type keyType = fieldArgTypes[0]; |
| final boolean any = fieldArgTypes.length < 2 || fieldArgTypes[1] == Object.class; |
| for (final Map.Entry<String, JsonValue> value : object.entrySet()) { |
| final JsonValue jsonValue = value.getValue(); |
| if (JsonNumber.class.isInstance(jsonValue) && any) { |
| map.put(value.getKey(), config.isUseBigDecimalForObjectNumbers() ? |
| JsonNumber.class.cast(jsonValue).bigDecimalValue() : toNumberValue(JsonNumber.class.cast(jsonValue))); |
| } else if (JsonString.class.isInstance(jsonValue) && any) { |
| map.put(value.getKey(), JsonString.class.cast(jsonValue).getString()); |
| } else { |
| map.put(convertTo(keyType, value.getKey()), toObject(null, jsonValue, fieldArgTypes[1], null, jsonPointer, Object.class)); |
| } |
| } |
| return map; |
| } |
| } |
| } else if (Map.class == type || HashMap.class == type || LinkedHashMap.class == type) { |
| final Map<String, Object> map = new LinkedHashMap<String, Object>(); |
| for (final Map.Entry<String, JsonValue> value : object.entrySet()) { |
| map.put(value.getKey(), toObject(null, value.getValue(), Object.class, null, jsonPointer, Object.class)); |
| } |
| return map; |
| } |
| } |
| if (classMapping == null) { |
| final String snippet = config.getSnippet().of(object); |
| final String description = ExceptionMessages.description(object); |
| throw new MapperException("Unable to map " + description + " to " + type + ": " + snippet); |
| } |
| |
| if (applyObjectConverter && classMapping.reader != null && (skippedConverters == null || !skippedConverters.contains(type))) { |
| final Collection<Class<?>> skipped = skippedConverters == null ? new ArrayList<>() : skippedConverters; |
| if (Class.class.isInstance(type)) { // more than likely, drop this check? |
| skipped.add(Class.class.cast(type)); |
| } |
| return classMapping.reader.fromJson(object, type, new SuppressConversionMappingParser(this, object, skipped)); |
| } |
| /* doesn't work yet |
| if (classMapping.adapter != null) { |
| return classMapping.adapter.from(t); |
| } |
| */ |
| |
| if (classMapping.factory == null) { |
| throw new MissingFactoryException(classMapping.clazz, object, config.getSnippet().of(object)); |
| } |
| if (config.isFailOnUnknown()) { |
| if (!classMapping.setters.keySet().containsAll(object.keySet())) { |
| throw new MapperException("(fail on unknown properties): " + |
| object.keySet().stream().filter(it -> !classMapping.setters.containsKey(it)).collect(joining(", ", "[", "]"))); |
| } |
| } |
| |
| Object t; |
| try { |
| if (classMapping.factory.getParameterTypes() == null || classMapping.factory.getParameterTypes().length == 0) { |
| t = classMapping.factory.create(null); |
| } else { |
| t = classMapping.factory.create(createParameters(classMapping, object, jsonPointer, e -> { |
| if (FactoryCreateException.class.isInstance(e)) { |
| throw FactoryCreateException.class.cast(e); |
| } |
| throw new FactoryCreateException(type, object, config.getSnippet().of(object), e); |
| })); |
| } |
| } catch (final FactoryCreateException e){ |
| throw e; |
| } catch (final Exception e) { |
| throw new FactoryCreateException(type, object, config.getSnippet().of(object), e); |
| } |
| |
| // store the new object under it's jsonPointer in case it gets referenced later |
| if (jsonPointers == null) { |
| if (classMapping.deduplicateObjects || config.isDeduplicateObjects()) { |
| jsonPointers = new HashMap<>(); |
| jsonPointers.put(jsonPointer == null ? "/" : jsonPointer.toString(), t); |
| } else { |
| jsonPointers = Collections.emptyMap(); |
| } |
| } else if (isDedup()) { |
| jsonPointers.put(jsonPointer == null ? "/" : jsonPointer.toString(), t); |
| } |
| |
| for (final Map.Entry<String, JsonValue> jsonEntry : object.entrySet()) { |
| final Mappings.Setter value = classMapping.setters.get(jsonEntry.getKey()); |
| if (value == null) { |
| continue; |
| } |
| |
| final JsonValue jsonValue = jsonEntry.getValue(); |
| final JsonValue.ValueType valueType = jsonValue != null ? jsonValue.getValueType() : null; |
| try { |
| if (JsonValue.class == value.paramType) { |
| value.writer.write(t, jsonValue); |
| continue; |
| } |
| if (jsonValue == null) { |
| continue; |
| } |
| |
| final AccessMode.Writer setterMethod = value.writer; |
| if (NULL == valueType) { // forced |
| setterMethod.write(t, null); |
| } else { |
| Object existingInstance = null; |
| if (config.isReadAttributeBeforeWrite()) { |
| final Mappings.Getter getter = classMapping.getters.get(jsonEntry.getKey()); |
| if (getter != null) { |
| try { |
| existingInstance = getter.reader.read(t); |
| } catch (final RuntimeException re) { |
| // backward compatibility |
| } |
| } |
| } |
| final Object convertedValue = toValue( |
| existingInstance, jsonValue, value.converter, value.itemConverter, |
| value.paramType, value.objectConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, jsonEntry.getKey()) : null, inType, |
| e -> { |
| if (SetterMappingException.class.isInstance(e)) { |
| throw SetterMappingException.class.cast(e); |
| } |
| final String snippet = config.getSnippet().of(jsonValue); |
| throw new SetterMappingException( |
| classMapping.clazz, jsonEntry.getKey(), value.writer.getType(), valueType, snippet, e); |
| }); |
| if (convertedValue != null) { |
| setterMethod.write(t, convertedValue); |
| } |
| } |
| } catch (final SetterMappingException alreadyHandled) { |
| throw alreadyHandled; |
| } catch (final Exception e) { |
| final String snippet = jsonValue == null? "null": config.getSnippet().of(jsonValue); |
| throw new SetterMappingException(classMapping.clazz, jsonEntry.getKey(), value.writer.getType(), valueType, snippet, e); |
| } |
| } |
| if (classMapping.anySetter != null) { |
| for (final Map.Entry<String, JsonValue> entry : object.entrySet()) { |
| final String key = entry.getKey(); |
| if (!classMapping.setters.containsKey(key)) { |
| try { |
| classMapping.anySetter.invoke(t, key, |
| toValue(null, entry.getValue(), null, null, |
| classMapping.anySetter.getGenericParameterTypes()[1], null, |
| isDedup() ? new JsonPointerTracker(jsonPointer, entry.getKey()) : null, type, |
| MapperException::new)); |
| } catch (final IllegalAccessException e) { |
| throw new IllegalStateException(e); |
| } catch (final InvocationTargetException e) { |
| throw new MapperException(e.getCause()); |
| } |
| } |
| } |
| } else if (classMapping.anyField != null) { |
| try { |
| classMapping.anyField.set(t, object.entrySet().stream() |
| .filter(it -> !classMapping.setters.containsKey(it.getKey())) |
| .collect(toMap(Map.Entry::getKey, e -> toValue(null, e.getValue(), null, null, |
| ParameterizedType.class.cast(classMapping.anyField.getGenericType()).getActualTypeArguments()[1], null, |
| isDedup() ? new JsonPointerTracker(jsonPointer, e.getKey()) : null, type, |
| MapperException::new)))); |
| } catch (final IllegalAccessException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| if (classMapping.mapAdder != null) { |
| object.entrySet().stream() |
| .filter(it -> !classMapping.setters.containsKey(it.getKey())) |
| .filter(it -> it.getValue().getValueType() != NULL) |
| .forEach(e -> { |
| final Object convertedValue = toValue( |
| null, e.getValue(), null, null, |
| classMapping.mapAdderType, null, |
| new JsonPointerTracker(jsonPointer, e.getKey()), inType, |
| MapperException::new); |
| if (convertedValue != null) { |
| try { |
| classMapping.mapAdder.invoke(t, e.getKey(), convertedValue); |
| } catch (final IllegalAccessException ex) { |
| throw new IllegalStateException(ex); |
| } catch (final InvocationTargetException ex) { |
| throw new MapperException(ex.getCause()); |
| } |
| } |
| }); |
| } |
| return t; |
| } |
| |
| private Number toNumberValue(JsonNumber jsonNumber) { |
| if (jsonNumber.isIntegral()) { |
| final int intValue = jsonNumber.intValue(); |
| final long longValue = jsonNumber.longValue(); |
| if (intValue == longValue) { |
| return intValue; |
| } |
| return longValue; |
| } |
| if (config.isUseBigDecimalForFloats()) { |
| return jsonNumber.bigDecimalValue(); |
| } |
| return jsonNumber.doubleValue(); |
| } |
| |
| private Object convertTo(final Adapter converter, final JsonValue jsonValue, final JsonPointerTracker jsonPointer, |
| final Type targetType) { |
| final JsonValue.ValueType valueType = jsonValue != null ? jsonValue.getValueType() : null; |
| |
| final AdapterKey key = getAdapterKey(converter); |
| if (key != null && JsonValue.class == key.getTo()) { |
| return converter.to(jsonValue); |
| } |
| |
| if (JsonValue.ValueType.OBJECT == valueType) { |
| if (JsonObject.class == key.getTo() || JsonStructure.class == key.getTo()) { |
| return converter.to(jsonValue.asJsonObject()); |
| } |
| final Object param; |
| try { |
| Type to = key.getTo(); |
| param = buildObject(to, JsonObject.class.cast(jsonValue), to instanceof Class, jsonPointer, getSkippedConverters()); |
| } catch (final Exception e) { |
| throw new MapperException(e); |
| } |
| return converter.to(param); |
| } |
| |
| if (NULL.equals(valueType)) { |
| return null; |
| } |
| if (STRING.equals(valueType)) { |
| if (key.getTo() == JsonString.class) { |
| return converter.to(JsonString.class.cast(jsonValue)); |
| } |
| return converter.to(JsonString.class.cast(jsonValue).getString()); |
| } |
| if (TRUE.equals(valueType) || FALSE.equals(valueType)) { |
| if (key != null) { |
| if (boolean.class == key.getTo() || Boolean.class == key.getTo()) { |
| return converter.to(Boolean.parseBoolean(jsonValue.toString())); |
| } |
| } |
| } |
| if (NUMBER.equals(valueType)) { |
| if (key != null) { |
| if (Long.class == key.getTo() || long.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).longValue()); |
| } else if (Integer.class == key.getTo() || int.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).intValue()); |
| } else if (Double.class == key.getTo() || double.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).doubleValue()); |
| } else if (Float.class == key.getTo() || float.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).doubleValue()); |
| } else if (BigInteger.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).bigIntegerValue()); |
| } else if (BigDecimal.class == key.getTo() || Number.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue).bigDecimalValue()); |
| } else if (JsonNumber.class == key.getTo()) { |
| return converter.to(JsonNumber.class.cast(jsonValue)); |
| } |
| } |
| } |
| if (ARRAY.equals(valueType)) { |
| if (JsonArray.class == key.getTo() || JsonStructure.class == key.getTo()) { |
| return converter.to(jsonValue.asJsonObject()); |
| } |
| if (TypeAwareAdapter.class.isInstance(converter)) { |
| final TypeAwareAdapter adapter = TypeAwareAdapter.class.cast(converter); |
| if (adapter.getFrom().equals(targetType)) { |
| return converter.to(readObject(jsonValue, adapter.getTo())); |
| } |
| } |
| return buildArray(key.getTo(), jsonValue.asJsonArray(), null, null, jsonPointer, null); |
| } |
| return converter.to(jsonValue.toString()); |
| |
| } |
| |
| private AdapterKey getAdapterKey(final Adapter converter) { |
| AdapterKey adapterKey = config.getReverseAdapters().get(converter); |
| |
| if (adapterKey == null) { |
| if (converter instanceof TypeAwareAdapter) { |
| return TypeAwareAdapter.class.cast(converter).getKey(); |
| |
| } else { |
| Class<?> current = converter.getClass(); |
| while (current != null && current != Object.class) { |
| final Type[] types = current.getGenericInterfaces(); |
| for (final Type t : types) { |
| if (!ParameterizedType.class.isInstance(t)) { |
| continue; |
| } |
| final ParameterizedType pt = ParameterizedType.class.cast(t); |
| if (Adapter.class == pt.getRawType()) { |
| final Type[] actualTypeArguments = pt.getActualTypeArguments(); |
| adapterKey = new AdapterKey(actualTypeArguments[0], actualTypeArguments[1]); |
| config.getReverseAdapters().putIfAbsent(converter, adapterKey); |
| return adapterKey; |
| } |
| } |
| current = current.getSuperclass(); |
| } |
| } |
| } |
| return adapterKey; |
| } |
| |
| |
| private Object toObject(final Object baseInstance, final JsonValue jsonValue, |
| final Type type, final Adapter itemConverter, final JsonPointerTracker jsonPointer, |
| final Type rootType) { |
| if (jsonValue == null) { |
| return null; |
| } |
| |
| JsonValue.ValueType valueType = jsonValue.getValueType(); |
| if (JsonValue.ValueType.NULL == valueType) { |
| return null; |
| } |
| |
| if (type == Boolean.class || type == boolean.class) { |
| if (JsonValue.ValueType.TRUE == valueType) { |
| return true; |
| } |
| if (JsonValue.ValueType.FALSE == valueType) { |
| return false; |
| } |
| final String snippet = config.getSnippet().of(jsonValue); |
| final String description = ExceptionMessages.description(valueType); |
| throw new MapperException("Unable to parse " + description + " to boolean: " + snippet); |
| } |
| |
| if (config.isTreatByteArrayAsBase64() && jsonValue.getValueType() == JsonValue.ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) { |
| return Base64.getDecoder().decode(((JsonString) jsonValue).getString()); |
| } |
| if (config.isTreatByteArrayAsBase64URL() && jsonValue.getValueType() == JsonValue.ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) { |
| return Base64.getUrlDecoder().decode(((JsonString) jsonValue).getString()); |
| } |
| |
| if (Object.class == type) { // handling specific types here to keep exception in standard handling |
| if (JsonValue.ValueType.TRUE == valueType) { |
| return true; |
| } |
| if (JsonValue.ValueType.FALSE == valueType) { |
| return false; |
| } |
| if (JsonNumber.class.isInstance(jsonValue)) { |
| return toNumberValue(JsonNumber.class.cast(jsonValue)); |
| } |
| if (JsonString.class.isInstance(jsonValue)) { |
| return JsonString.class.cast(jsonValue).getString(); |
| } |
| } |
| |
| if (type == Character.class || type == char.class) { |
| return convertTo(Class.class.cast(type), (JsonString.class.cast(jsonValue).getString())); |
| } |
| |
| if (JsonObject.class.isInstance(jsonValue)) { |
| if (JsonObject.class == type || JsonStructure.class == type || JsonValue.class == type) { |
| return jsonValue; |
| } |
| final boolean typedAdapter = !ConverterAdapter.class.isInstance(itemConverter) && TypeAwareAdapter.class.isInstance(itemConverter); |
| final Object object = buildObject( |
| baseInstance != null ? baseInstance.getClass() : ( |
| typedAdapter ? TypeAwareAdapter.class.cast(itemConverter).getTo() : type), |
| JsonObject.class.cast(jsonValue), type instanceof Class, |
| jsonPointer, getSkippedConverters()); |
| return typedAdapter ? itemConverter.to(object) : object; |
| } else if (JsonArray.class.isInstance(jsonValue)) { |
| if (JsonArray.class == type || JsonStructure.class == type || JsonValue.class == type) { |
| return jsonValue; |
| } |
| return buildArray(type, JsonArray.class.cast(jsonValue), itemConverter, null, jsonPointer, rootType); |
| } else if (JsonNumber.class.isInstance(jsonValue)) { |
| if (JsonNumber.class == type || JsonValue.class == type) { |
| return jsonValue; |
| } |
| |
| final JsonNumber number = JsonNumber.class.cast(jsonValue); |
| |
| if (type == Long.class || type == long.class) { |
| return number.longValueExact(); |
| } |
| |
| if (type == Float.class || type == float.class) { |
| return (float) number.doubleValue(); |
| } |
| |
| if (type == Double.class || type == double.class) { |
| return number.doubleValue(); |
| } |
| |
| if (type == BigInteger.class) { |
| return number.bigIntegerValue(); |
| } |
| |
| if (type == BigDecimal.class || Number.class == type) { |
| return number.bigDecimalValue(); |
| } |
| |
| if (type == Integer.class || type == int.class) { |
| return number.intValueExact(); |
| } |
| |
| if (type == Short.class || type == short.class) { |
| final int intValue = number.intValue(); |
| short shortVal = (short) intValue; |
| if (intValue != shortVal) { |
| throw new java.lang.ArithmeticException("Overflow"); |
| } |
| return shortVal; |
| } |
| |
| if (type == Byte.class || type == byte.class) { |
| final int intValue = number.intValueExact(); |
| Validator.validateByte(intValue); |
| return (byte) intValue; |
| } |
| |
| } else if (JsonString.class.isInstance(jsonValue)) { |
| if (JsonString.class == type || JsonValue.class == type) { |
| return jsonValue; |
| } |
| |
| final String string = JsonString.class.cast(jsonValue).getString(); |
| if (itemConverter == null) { |
| // check whether we have a jsonPointer to a previously deserialised object |
| if (isDedup() && !String.class.equals(type)) { |
| Object o = jsonPointers == null ? null : jsonPointers.get(string); |
| if (o != null) { |
| return o; |
| } |
| } |
| return convertTo(type, string); |
| } |
| return itemConverter.to(string); |
| } |
| |
| final String snippet = config.getSnippet().of(jsonValue); |
| final String description = ExceptionMessages.description(valueType); |
| throw new MapperException("Unable to parse " + description + " to " + type + ": " + snippet); |
| } |
| |
| private Object buildArray(final Type type, final JsonArray jsonArray, final Adapter itemConverter, |
| 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()) { |
| final Class<?> componentType = clazz.getComponentType(); |
| return buildArrayWithComponentType(jsonArray, componentType, itemConverter, jsonPointer, rootType); |
| } |
| if (Collection.class.isAssignableFrom(clazz)) { |
| final Mappings.CollectionMapping mapping = mappings.findCollectionMapping( |
| new JohnzonParameterizedType(clazz, Object.class), rootType); |
| if (mapping != null) { |
| return mapCollection(mapping, jsonArray, itemConverter, objectConverter, jsonPointer, rootType); |
| } |
| } |
| } |
| |
| if (ParameterizedType.class.isInstance(type)) { |
| final ParameterizedType genericType = ParameterizedType.class.cast(type); |
| if (Stream.class == genericType.getRawType()) { |
| return Stream.of(1).flatMap(seed -> Collection.class.cast(buildArray( |
| new JohnzonParameterizedType(List.class, genericType.getActualTypeArguments()), |
| jsonArray, itemConverter, objectConverter, jsonPointer, rootType)).stream()); |
| } |
| |
| final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(genericType, rootType); |
| if (mapping != null) { |
| return mapCollection(mapping, jsonArray, itemConverter, objectConverter, jsonPointer, rootType); |
| } |
| } |
| |
| if (GenericArrayType.class.isInstance(type)) { |
| Type genericComponentType = GenericArrayType.class.cast(type).getGenericComponentType(); |
| while (ParameterizedType.class.isInstance(genericComponentType)) { |
| genericComponentType = ParameterizedType.class.cast(genericComponentType).getRawType(); |
| } |
| if (Class.class.isInstance(genericComponentType)) { |
| return buildArrayWithComponentType(jsonArray, Class.class.cast(genericComponentType), itemConverter, jsonPointer, rootType); |
| } // else: fail for now |
| } |
| |
| if (Object.class == type) { |
| return buildArray(ANY_LIST, jsonArray, null, null, jsonPointer, rootType); |
| } |
| |
| // guess we don't want to map stream impls - keep it lazy since it is the only advantage to have streams there |
| if (IntStream.class == type) { |
| return Stream.of(1).flatMapToInt(seed -> IntStream.of(int[].class.cast( |
| buildArray(int[].class, jsonArray, null, null, jsonPointer, rootType)))); |
| } |
| if (LongStream.class == type) { |
| return Stream.of(1).flatMapToLong(seed -> LongStream.of(long[].class.cast( |
| buildArray(long[].class, jsonArray, null, null, jsonPointer, rootType)))); |
| } |
| if (DoubleStream.class == type) { |
| return Stream.of(1).flatMapToDouble(seed -> DoubleStream.of(double[].class.cast( |
| buildArray(double[].class, jsonArray, null, null, jsonPointer, rootType)))); |
| } |
| |
| throw new UnsupportedOperationException("type " + type + " not supported"); |
| } |
| |
| private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType, final Adapter itemConverter, |
| final JsonPointerTracker jsonPointer, final Type rootType) { |
| |
| if (boolean.class == componentType) { |
| boolean[] array = new boolean[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to boolean[] has null value at index " + i); |
| } |
| array[i] = (boolean) object; |
| i++; |
| } |
| return array; |
| } |
| if (byte.class == componentType) { |
| byte[] array = new byte[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to byte[] has null value at index " + i); |
| } |
| array[i] = (byte) object; |
| i++; |
| } |
| return array; |
| } |
| if (char.class == componentType) { |
| char[] array = new char[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to char[] has null value at index " + i); |
| } |
| array[i] = (char) object; |
| i++; |
| } |
| return array; |
| } |
| if (short.class == componentType) { |
| short[] array = new short[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to short[] has null value at index " + i); |
| } |
| array[i] = (short) object; |
| i++; |
| } |
| return array; |
| } |
| if (int.class == componentType) { |
| int[] array = new int[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to int[] has null value at index " + i); |
| } |
| array[i] = (int) object; |
| i++; |
| } |
| return array; |
| } |
| if (long.class == componentType) { |
| long[] array = new long[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to long[] has null value at index " + i); |
| } |
| array[i] = (long) object; |
| i++; |
| } |
| return array; |
| } |
| if (float.class == componentType) { |
| float[] array = new float[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to float[] has null value at index " + i); |
| } |
| array[i] = (float) object; |
| i++; |
| } |
| return array; |
| } |
| if (double.class == componentType) { |
| double[] array = new double[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| final Object object = toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| if (object == null) { |
| throw new IllegalStateException("json array mapped to double[] has null value at index " + i); |
| } |
| array[i] = (double) object; |
| i++; |
| } |
| return array; |
| } |
| |
| // wrapper types |
| if (Boolean.class == componentType) { |
| Boolean[] array = new Boolean[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Boolean) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Byte.class == componentType) { |
| Byte[] array = new Byte[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Byte) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Character.class == componentType) { |
| Character[] array = new Character[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Character) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Short.class == componentType) { |
| Short[] array = new Short[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Short) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Integer.class == componentType) { |
| Integer[] array = new Integer[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Integer) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Long.class == componentType) { |
| Long[] array = new Long[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Long) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Float.class == componentType) { |
| Float[] array = new Float[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Float) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| if (Double.class == componentType) { |
| Double[] array = new Double[jsonArray.size()]; |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| array[i] = (Double) toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType); |
| i++; |
| } |
| return array; |
| } |
| |
| // for all the rest we have to rely on reflection :( |
| final Object array = Array.newInstance(componentType, jsonArray.size()); |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| Array.set(array, i, toObject(null, value, componentType, itemConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType)); |
| i++; |
| } |
| return array; |
| } |
| |
| 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 Collection collection; |
| |
| if (SortedSet.class == mapping.raw || NavigableSet.class == mapping.raw || TreeSet.class == mapping.raw) { |
| collection = new TreeSet<T>(); |
| } else if (Set.class == mapping.raw || HashSet.class == mapping.raw) { |
| collection = new HashSet<T>(jsonArray.size()); |
| } else if (Queue.class == mapping.raw || ArrayBlockingQueue.class == mapping.raw) { |
| collection = new ArrayBlockingQueue<T>(jsonArray.size()); |
| } else if (List.class == mapping.raw || Collection.class == mapping.raw || ArrayList.class == mapping.raw || EnumSet.class == mapping.raw) { |
| collection = new ArrayList<T>(jsonArray.size()); |
| } else if (LinkedHashSet.class == mapping.raw) { |
| collection = new LinkedHashSet<T>(jsonArray.size()); |
| } else if (LinkedList.class == mapping.raw) { |
| collection = new LinkedList<T>(); |
| } else if (Deque.class == mapping.raw || ArrayDeque.class == mapping.raw) { |
| collection = new ArrayDeque(jsonArray.size()); |
| } else if (PriorityQueue.class == mapping.raw) { |
| collection = new PriorityQueue(jsonArray.size()); |
| } else { |
| throw new IllegalStateException("not supported collection type: " + mapping.raw.getName()); |
| } |
| |
| int i = 0; |
| for (final JsonValue value : jsonArray) { |
| collection.add(JsonValue.NULL.equals(value) |
| ? null |
| : toValue(null, value, null, itemConverter, mapping.arg, objectConverter, |
| isDedup() ? new JsonPointerTracker(jsonPointer, i) : null, rootType, MapperException::new)); |
| i++; |
| } |
| |
| if (EnumSet.class == mapping.raw) { |
| if (!config.isSupportEnumContainerDeserialization()) { |
| throw new MapperException("Enum container deserialization disabled, " + |
| "set supportEnumContainerDeserialization=true to enable it"); |
| } |
| if (collection.isEmpty()) { |
| return EnumSet.noneOf(Class.class.cast(mapping.arg)); |
| } else if (collection.size() == 1) { |
| return Collection.class.cast(EnumSet.of(Enum.class.cast(collection.iterator().next()))); |
| } else { |
| final List<Enum> list = List.class.cast(collection); |
| return Collection.class.cast(EnumSet.of(list.get(0), list.subList(1, list.size()).toArray(new Enum[list.size() - 1]))); |
| } |
| } |
| |
| return collection; |
| } |
| |
| |
| private Object[] createParameters(final Mappings.ClassMapping mapping, final JsonObject object, JsonPointerTracker jsonPointer, |
| final Function<Exception, RuntimeException> onException) { |
| final int length = mapping.factory.getParameterTypes().length; |
| final Object[] objects = new Object[length]; |
| |
| for (int i = 0; i < length; i++) { |
| final String paramName = mapping.factory.getParameterNames()[i]; |
| final Type parameterType = mapping.factory.getParameterTypes()[i]; |
| objects[i] = toValue(null, |
| object.get(paramName), |
| mapping.factory.getParameterConverter()[i], |
| mapping.factory.getParameterItemConverter()[i], |
| parameterType, |
| mapping.factory.getObjectConverter()[i], |
| isDedup() ? new JsonPointerTracker(jsonPointer, paramName) : null, |
| mapping.clazz, //X TODO ObjectConverter in @JohnzonConverter with Constructors! |
| onException); |
| if (objects[i] == null) { |
| objects[i] = getPrimitiveDefault(parameterType); |
| } |
| } |
| |
| return objects; |
| } |
| |
| 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 Function<Exception, RuntimeException> onException) { |
| |
| if (objectConverter != null) { |
| return objectConverter.fromJson(jsonValue, type, this); |
| } |
| |
| try { |
| return converter == null ? |
| toObject(baseInstance, jsonValue, type, itemConverter, jsonPointer, rootType) : |
| convertTo(converter, jsonValue, jsonPointer, type); |
| } catch (final Exception e) { |
| if (e instanceof MapperException) { |
| throw e; |
| } |
| throw onException.apply(e); |
| } |
| } |
| |
| |
| /** |
| * @deprecated see MapperConfig |
| */ |
| private Object convertTo(final Type aClass, final String text) { |
| if (Object.class == aClass || String.class == aClass) { |
| return text; |
| } |
| final Adapter converter = findAdapter(aClass); |
| Method method = valueOfs.get(aClass); |
| if (method == null && Class.class.isInstance(aClass)) { // handle primitives |
| final Class cast = Class.class.cast(aClass); |
| try { |
| method = cast.getMethod("valueOf", String.class); |
| if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) { |
| valueOfs.putIfAbsent(cast, method); |
| } else { |
| method = null; |
| } |
| } catch (final NoSuchMethodException e) { |
| // if a real primitive (very unlikely) try the wrapper |
| if (char.class == aClass) { |
| return CHARACTER_CONVERTER.fromString(text); |
| } |
| try { |
| return convertTo(Class.class.cast(cast.getField("TYPE").get(null)), text); |
| } catch (final Exception e1) { |
| // no-op |
| } |
| // no-op |
| } |
| } |
| if (method != null) { |
| try { |
| return method.invoke(null, text); |
| } catch (final IllegalAccessException e) { |
| throw new IllegalStateException(e); |
| } catch (final InvocationTargetException e) { |
| throw new MapperException(e.getCause()); |
| } |
| } |
| if (converter == null) { |
| if (ParameterizedType.class.isInstance(aClass)) { |
| return convertTo(ParameterizedType.class.cast(aClass).getRawType(), text); |
| } |
| 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); |
| } |
| |
| /** |
| * @deprecated see MapperConfig - it is acually reversed so maybe not deprecated after all? |
| */ |
| private Adapter findAdapter(final Type aClass) { |
| if (config.getNoParserAdapterTypes().contains(aClass)) { |
| return null; |
| } |
| final Adapter<?, ?> converter = config.getAdapters().get(new AdapterKey(aClass, String.class, true)); |
| if (converter != null) { |
| return converter; |
| } |
| if (Class.class.isInstance(aClass)) { |
| final Class<?> clazz = Class.class.cast(aClass); |
| if (Enum.class.isAssignableFrom(clazz)) { |
| final Adapter<?, ?> enumConverter = new ConverterAdapter(config.getEnumConverterFactory().apply(clazz), clazz); |
| config.getAdapters().putIfAbsent(new AdapterKey(String.class, aClass), enumConverter); |
| return enumConverter; |
| } |
| } |
| final List<AdapterKey> matched = config.getAdapters().adapterKeys().stream() |
| .filter(k -> k.isAssignableFrom(aClass)) |
| .collect(toList()); |
| if (matched.size() == 1) { |
| final Adapter<?, ?> adapter = config.getAdapters().get(matched.iterator().next()); |
| if (TypeAwareAdapter.class.isInstance(adapter)) { |
| config.getAdapters().put(new AdapterKey(aClass, TypeAwareAdapter.class.cast(adapter).getTo()), adapter); |
| } |
| return adapter; |
| } |
| config.getNoParserAdapterTypes().add(aClass); |
| return null; |
| } |
| |
| /** |
| * Internal class to suppress {@link ObjectConverter} lookup if and only if |
| * 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; |
| private final Collection<Class<?>> skippedConverters; |
| |
| public SuppressConversionMappingParser(final MappingParserImpl delegate, final JsonObject suppressConversionFor, |
| final Collection<Class<?>> skippedConverters) { |
| this.delegate = delegate; |
| this.suppressConversionFor = suppressConversionFor; |
| this.skippedConverters = skippedConverters; |
| } |
| |
| @Override |
| public Collection<Class<?>> getSkippedConverters() { |
| return skippedConverters; |
| } |
| |
| @Override |
| public <T> T readObject(final Type targetType) { |
| return delegate.readObject(targetType); |
| } |
| |
| @Override |
| public <T> T readObject(final JsonValue jsonValue, final Type targetType) { |
| final Collection<Class<?>> skippedConverters = getSkippedConverters(); |
| if (suppressConversionFor == jsonValue) { |
| return delegate.readObject(jsonValue, targetType, false, skippedConverters); |
| } |
| final boolean useConverters = (Class.class.isInstance(targetType) && |
| (skippedConverters == null || skippedConverters.stream().noneMatch(it -> it.isAssignableFrom(Class.class.cast(targetType))))) || |
| ParameterizedType.class.isInstance(targetType); |
| return delegate.readObject(jsonValue, targetType, useConverters, skippedConverters); |
| } |
| } |
| } |