in johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java [233:475]
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));
}
}
}
final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(type);
if (classMapping != null && classMapping.polymorphicDeserializedTypeResolver != null && inType instanceof Class) {
Class<?> nestedType = classMapping.polymorphicDeserializedTypeResolver.apply(object, (Class<?>) inType);
if (nestedType != null && nestedType != inType) {
return buildObject(nestedType, object, applyObjectConverter, jsonPointer, skippedConverters);
}
}
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;
}