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/08/16 12:53:27 UTC
[johnzon] branch master updated: JOHNZON-261 JOHNZON-260 better
optional handling
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
The following commit(s) were added to refs/heads/master by this push:
new cd66727 JOHNZON-261 JOHNZON-260 better optional handling
cd66727 is described below
commit cd66727ab3244223a9b6c69227e74c7f7e94edfb
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Fri Aug 16 14:53:22 2019 +0200
JOHNZON-261 JOHNZON-260 better optional handling
---
.../org/apache/johnzon/jsonb/JohnzonBuilder.java | 1 -
.../org/apache/johnzon/jsonb/JsonbAccessMode.java | 98 ++++++++++++++++------
.../jsonb/reflect/GenericArrayTypeImpl.java | 55 ++++++++++++
.../java/org/apache/johnzon/mapper/Mapper.java | 2 +-
.../apache/johnzon/mapper/MappingParserImpl.java | 13 ++-
.../java/org/apache/johnzon/mapper/Mappings.java | 24 +++++-
6 files changed, 160 insertions(+), 33 deletions(-)
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 6f7572e..03410df 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
@@ -629,7 +629,6 @@ public class JohnzonBuilder implements JsonbBuilder {
}));
addDateFormatConfigConverters(converters, zoneIDUTC);
-
converters.forEach((k, v) -> builder.addAdapter(k.getFrom(), k.getTo(), v));
return converters;
}
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 b723229..d999e35 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
@@ -29,6 +29,7 @@ import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -95,6 +96,7 @@ 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.order.PerHierarchyAndLexicographicalOrderFieldComparator;
+import org.apache.johnzon.jsonb.reflect.GenericArrayTypeImpl;
import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
@@ -462,20 +464,41 @@ public class JsonbAccessMode implements AccessMode, Closeable {
// handle optionals since mapper is still only java 7
final Type type;
final Function<Object, Object> reader;
- if (isOptional(finalReader)) {
- type = ParameterizedType.class.cast(finalReader.getType()).getActualTypeArguments()[0];
+ final Type readerType = finalReader.getType();
+ if (isOptional(readerType)) {
+ type = ParameterizedType.class.cast(readerType).getActualTypeArguments()[0];
reader = i -> ofNullable(finalReader.read(i)).map(o -> Optional.class.cast(o).orElse(null)).orElse(null);
- } else if (OptionalInt.class == finalReader.getType()) {
- type = int.class;
- reader = i -> OptionalInt.class.cast(finalReader.read(i)).orElse(0);
- } else if (OptionalLong.class == finalReader.getType()) {
- type = long.class;
- reader = i -> OptionalLong.class.cast(finalReader.read(i)).orElse(0);
- } else if (OptionalDouble.class == finalReader.getType()) {
- type = double.class;
- reader = i -> OptionalDouble.class.cast(finalReader.read(i)).orElse(0);
+ } else if (OptionalInt.class == readerType) {
+ type = Integer.class;
+ reader = i -> {
+ final OptionalInt optionalInt = OptionalInt.class.cast(finalReader.read(i));
+ return optionalInt == null || !optionalInt.isPresent() ? null : optionalInt.getAsInt();
+ };
+ } else if (OptionalLong.class == readerType) {
+ type = Long.class;
+ reader = i -> {
+ final OptionalLong optionalLong = OptionalLong.class.cast(finalReader.read(i));
+ return optionalLong == null || !optionalLong.isPresent() ? null : optionalLong.getAsLong();
+ };
+ } else if (OptionalDouble.class == readerType) {
+ type = Double.class;
+ reader = i -> {
+ final OptionalDouble optionalDouble = OptionalDouble.class.cast(finalReader.read(i));
+ return optionalDouble == null || !optionalDouble.isPresent() ? null : optionalDouble.getAsDouble();
+ };
+ } else if (isOptionalArray(finalReader)) {
+ final Type optionalUnwrappedType = findOptionalType(GenericArrayType.class.cast(readerType).getGenericComponentType());
+ type = new GenericArrayTypeImpl(optionalUnwrappedType);
+ reader = i -> {
+ final Object[] optionals = Object[].class.cast(finalReader.read(i));
+ return optionals == null ?
+ null : Stream.of(optionals)
+ .map(Optional.class::cast)
+ .map(o -> o.orElse(null))
+ .toArray();
+ };
} else {
- type = finalReader.getType();
+ type = readerType;
reader = finalReader::read;
}
@@ -566,20 +589,34 @@ public class JsonbAccessMode implements AccessMode, Closeable {
// handle optionals since mapper is still only java 7
final Type type;
final BiConsumer<Object, Object> writer;
- if (isOptional(initialWriter)) {
- type = ParameterizedType.class.cast(initialWriter.getType()).getActualTypeArguments()[0];
+ final Type writerType = initialWriter.getType();
+ if (isOptional(writerType)) {
+ type = findOptionalType(writerType);
writer = (i, val) -> finalWriter.write(i, Optional.ofNullable(val));
- } else if (OptionalInt.class == initialWriter.getType()) {
- type = int.class;
- writer = (i, val) -> finalWriter.write(i, OptionalInt.of(Number.class.cast(val).intValue()));
- } else if (OptionalLong.class == initialWriter.getType()) {
- type = long.class;
- writer = (i, val) -> finalWriter.write(i, OptionalLong.of(Number.class.cast(val).longValue()));
- } else if (OptionalDouble.class == initialWriter.getType()) {
- type = double.class;
- writer = (i, val) -> finalWriter.write(i, OptionalDouble.of(Number.class.cast(val).doubleValue()));
+ } else if (OptionalInt.class == writerType) {
+ type = Integer.class;
+ writer = (i, value) -> finalWriter.write(i, value == null ?
+ OptionalInt.empty() : OptionalInt.of(Number.class.cast(value).intValue()));
+ } else if (OptionalLong.class == writerType) {
+ type = Long.class;
+ writer = (i, value) -> finalWriter.write(i, value == null ?
+ OptionalLong.empty() : OptionalLong.of(Number.class.cast(value).longValue()));
+ } else if (OptionalDouble.class == writerType) {
+ type = Double.class;
+ writer = (i, value) -> finalWriter.write(i, value == null ?
+ OptionalDouble.empty() : OptionalDouble.of(Number.class.cast(value).doubleValue()));
+ } else if (isOptionalArray(initialWriter)) {
+ final Type optionalUnwrappedType = findOptionalType(GenericArrayType.class.cast(writerType).getGenericComponentType());
+ type = new GenericArrayTypeImpl(optionalUnwrappedType);
+ writer = (i, value) -> {
+ if (value != null) {
+ finalWriter.write(i, Stream.of(Object[].class.cast(value))
+ .map(Optional::ofNullable)
+ .toArray(Optional[]::new));
+ }
+ };
} else {
- type = initialWriter.getType();
+ type = writerType;
writer = finalWriter::write;
}
@@ -703,8 +740,17 @@ public class JsonbAccessMode implements AccessMode, Closeable {
return cache;
}
- private boolean isOptional(final DecoratedType value) {
- return ParameterizedType.class.isInstance(value.getType()) && Optional.class == ParameterizedType.class.cast(value.getType()).getRawType();
+ private Type findOptionalType(final Type writerType) {
+ return ParameterizedType.class.cast(writerType).getActualTypeArguments()[0];
+ }
+
+ private boolean isOptional(final Type type) {
+ return ParameterizedType.class.isInstance(type) && Optional.class == ParameterizedType.class.cast(type).getRawType();
+ }
+
+ private boolean isOptionalArray(final DecoratedType value) {
+ return GenericArrayType.class.isInstance(value.getType()) &&
+ isOptional(GenericArrayType.class.cast(value.getType()).getGenericComponentType());
}
private boolean isTransient(final DecoratedType dt, final PropertyVisibilityStrategy visibility) {
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/GenericArrayTypeImpl.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/GenericArrayTypeImpl.java
new file mode 100644
index 0000000..d0d1a4d
--- /dev/null
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/GenericArrayTypeImpl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.jsonb.reflect;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+public class GenericArrayTypeImpl implements GenericArrayType {
+ private final Type componentType;
+
+ public GenericArrayTypeImpl(final Type componentType) {
+ this.componentType = componentType;
+ }
+
+ @Override
+ public Type getGenericComponentType() {
+ return componentType;
+ }
+
+ @Override
+ public int hashCode() {
+ return componentType.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj instanceof GenericArrayType) {
+ return ((GenericArrayType) obj).getGenericComponentType().equals(componentType);
+ }
+ return false;
+
+ }
+
+ public String toString() {
+ return componentType + "[]";
+ }
+}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 18b7398..e8ae5cc 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -228,7 +228,7 @@ public class Mapper implements Closeable {
}
private void writeObject(final Object object, final JsonGenerator generator, final Collection<String> ignored, JsonPointerTracker jsonPointer) {
- MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, generator, mappings, jsonPointer != null);
+ final MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, generator, mappings, jsonPointer != null);
mappingGenerator.doWriteObject(object, generator, true, ignored, jsonPointer);
}
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 a182a5a..e32b650 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
@@ -36,6 +36,7 @@ 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;
@@ -713,6 +714,16 @@ public class MappingParserImpl implements MappingParser {
}
}
+ 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);
}
@@ -861,7 +872,7 @@ public class MappingParserImpl implements MappingParser {
}
if (Integer.class == componentType) {
Integer[] array = new Integer[jsonArray.size()];
- Integer i = 0;
+ int i = 0;
for (final JsonValue value : jsonArray) {
array[i] = (Integer) toObject(null, value, componentType, itemConverter,
isDeduplicateObjects ? new JsonPointerTracker(jsonPointer, i) : null, rootType);
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 1e7ea18..1ecbe44 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -29,6 +29,7 @@ import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -488,7 +489,9 @@ public class Mappings {
final Type param = value.getType();
final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null;
final Setter setter = new Setter(
- value, isPrimitive(param), returnType != null && returnType.isArray(), resolve(param, rootClass),
+ value, isPrimitive(param),
+ (returnType != null && returnType.isArray()) || GenericArrayType.class.isInstance(value.getType()),
+ resolve(param, rootClass),
findConverter(copyDate, value), value.findObjectConverterReader(),
writeIgnore != null ? writeIgnore.minVersion() : -1);
setters.put(key, setter);
@@ -506,7 +509,7 @@ public class Mappings {
final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
final Getter getter = new Getter(value, returnType == Object.class, isPrimitive(returnType),
- returnType != null && returnType.isArray(),
+ (returnType != null && returnType.isArray()) || GenericArrayType.class.isInstance(value.getType()),
(pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
|| (returnType != null && Collection.class.isAssignableFrom(returnType)),
(pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
@@ -710,15 +713,28 @@ public class Mappings {
public MapUnwrapperWriter(final Map<String, Setter> writers, final String[] paths) {
this.writers = writers;
this.paths = paths;
- this.componentTypes = new HashMap<String, Class<?>>();
+ this.componentTypes = new HashMap<>();
for (final Map.Entry<String, Setter> setter : writers.entrySet()) {
if (setter.getValue().array) {
- componentTypes.put(setter.getKey(), Class.class.cast(setter.getValue().paramType).getComponentType());
+ componentTypes.put(setter.getKey(),
+ Class.class.isInstance(setter.getValue().paramType) ?
+ Class.class.cast(setter.getValue().paramType).getComponentType() :
+ cast(GenericArrayType.class.cast(setter.getValue().paramType).getGenericComponentType()));
}
}
}
+ private Class<?> cast(final Type genericComponentType) {
+ if (Class.class.isInstance(genericComponentType)) {
+ return Class.class.cast(genericComponentType);
+ }
+ if (ParameterizedType.class.isInstance(genericComponentType)) {
+ return cast(ParameterizedType.class.cast(genericComponentType).getRawType());
+ }
+ throw new UnsupportedOperationException("Unsupported type: " + genericComponentType);
+ }
+
@Override
public void write(final Object instance, final Object value) {
Map<String, Object> nested = null;