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 2015/12/07 17:37:57 UTC
[1/2] incubator-johnzon git commit: more of jsonb - missing plain
value handling
Repository: incubator-johnzon
Updated Branches:
refs/heads/master 65564700a -> a409da7d1
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
index 6bb1f1f..c733f3a 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/reflection/Mappings.java
@@ -36,6 +36,7 @@ import java.math.BigInteger;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -212,10 +213,12 @@ public class Mappings {
final Class<?> collectionType;
if (List.class.isAssignableFrom(r)) {
collectionType = List.class;
- }else if (SortedSet.class.isAssignableFrom(r)) {
+ } else if (SortedSet.class.isAssignableFrom(r)) {
collectionType = SortedSet.class;
} else if (Set.class.isAssignableFrom(r)) {
collectionType = Set.class;
+ } else if (Deque.class.isAssignableFrom(r)) {
+ collectionType = Deque.class;
} else if (Queue.class.isAssignableFrom(r)) {
collectionType = Queue.class;
} else if (Collection.class.isAssignableFrom(r)) {
@@ -286,8 +289,11 @@ public class Mappings {
}
final Class<?> clazz = findModelClass(inClazz);
- final Map<String, Getter> getters = newOrderedMap();
- final Map<String, Setter> setters = newOrderedMap();
+ Comparator<String> fieldComparator = accessMode.fieldComparator(inClazz);
+ fieldComparator = fieldComparator == null ? fieldOrdering : fieldComparator;
+
+ final Map<String, Getter> getters = fieldComparator == null ? newOrderedMap(Getter.class) : new TreeMap<String, Getter>(fieldComparator);
+ final Map<String, Setter> setters = fieldComparator == null ? newOrderedMap(Setter.class) : new TreeMap<String, Setter>(fieldComparator);
final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz);
final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz);
@@ -339,7 +345,7 @@ public class Mappings {
return clazz;
}
- private <T> Map<String, T> newOrderedMap() {
+ private <T> Map<String, T> newOrderedMap(final Class<T> value) {
return fieldOrdering != null ? new TreeMap<String, T>(fieldOrdering) : new HashMap<String, T>();
}
@@ -400,8 +406,8 @@ public class Mappings {
}
// build "this" model
- final Map<String, Getter> objectGetters = newOrderedMap();
- final Map<String, Setter> objectSetters = newOrderedMap();
+ final Map<String, Getter> objectGetters = newOrderedMap(Getter.class);
+ final Map<String, Setter> objectSetters = newOrderedMap(Setter.class);
for (final JohnzonVirtualObject.Field f : o.fields()) {
final String name = f.value();
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
index a6c3267..d992725 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
@@ -377,10 +377,7 @@ public class MapperTest {
@Test(expected = MapperException.class)
public void literalFail2() {
-
- final Bool2 instance = new MapperBuilder().build().readObject(new ByteArrayInputStream("{\"map\":{\"key\":\"true\"}}".getBytes()),
- Bool2.class);
-
+ new MapperBuilder().build().readObject(new ByteArrayInputStream("{\"map\":{\"key\":\"true\"}}".getBytes()), Bool2.class);
}
@Test
[2/2] incubator-johnzon git commit: more of jsonb - missing plain
value handling
Posted by rm...@apache.org.
more of jsonb - missing plain value handling
Project: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/commit/a409da7d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/tree/a409da7d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-johnzon/diff/a409da7d
Branch: refs/heads/master
Commit: a409da7d1121c3d92e09d8d2ba1f123413051f73
Parents: 6556470
Author: Romain Manni-Bucau <rm...@gmail.com>
Authored: Mon Dec 7 17:38:45 2015 +0100
Committer: Romain Manni-Bucau <rm...@gmail.com>
Committed: Mon Dec 7 17:38:45 2015 +0100
----------------------------------------------------------------------
.../org/apache/johnzon/core/JsonLongImpl.java | 2 +-
.../org/apache/johnzon/core/JsonReaderImpl.java | 5 +-
.../org/apache/johnzon/jsonb/JohnsonJsonb.java | 135 +-
.../apache/johnzon/jsonb/JohnzonBuilder.java | 15 +-
.../apache/johnzon/jsonb/JsonbAccessMode.java | 145 +-
.../johnzon/jsonb/DefaultMappingTest.java | 1312 ++++++++++++++++++
.../java/org/apache/johnzon/mapper/Mapper.java | 107 +-
.../johnzon/mapper/access/AccessMode.java | 2 +
.../johnzon/mapper/access/BaseAccessMode.java | 11 +-
.../johnzon/mapper/reflection/Mappings.java | 18 +-
.../org/apache/johnzon/mapper/MapperTest.java | 5 +-
11 files changed, 1648 insertions(+), 109 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-core/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonLongImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
index 2f013f7..1a85b0b 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
@@ -23,7 +23,7 @@ import java.math.BigInteger;
import javax.json.JsonNumber;
-final class JsonLongImpl implements JsonNumber {
+public final class JsonLongImpl implements JsonNumber {
private final long value;
JsonLongImpl(final long value) {
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
index 86d7708..bba027c 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -43,7 +43,8 @@ class JsonReaderImpl implements JsonReader {
if (!parser.hasNext()) {
throw new IllegalStateException("Nothing to read");
}
- switch (parser.next()) {
+ final JsonParser.Event next = parser.next();
+ switch (next) {
case START_OBJECT:
final JsonObjectBuilder objectBuilder = new JsonObjectBuilderImpl();
parseObject(objectBuilder);
@@ -62,7 +63,7 @@ class JsonReaderImpl implements JsonReader {
return arrayBuilder.build();
default:
close();
- throw new JsonParsingException("Unknown structure: " + parser.next(), parser.getLocation());
+ throw new JsonParsingException("Unknown structure: " + next, parser.getLocation());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java
----------------------------------------------------------------------
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java
index 85f9bd1..ec5e960 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java
@@ -19,6 +19,8 @@
package org.apache.johnzon.jsonb;
import org.apache.johnzon.mapper.Mapper;
+import org.apache.johnzon.mapper.MapperException;
+import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbException;
@@ -30,6 +32,7 @@ import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
+import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.CharBuffer;
@@ -44,28 +47,52 @@ public class JohnsonJsonb implements Jsonb {
@Override
public <T> T fromJson(final String str, final Class<T> type) throws JsonbException {
- return delegate.readObject(str, type);
+ try {
+ if (isArray(type)) {
+ return delegate.readTypedArray(new StringReader(str), type.getComponentType(), type);
+ } else if (Collection.class.isAssignableFrom(type)) {
+ return (T) delegate.readCollection(new StringReader(str), new JohnzonParameterizedType(type, Object.class));
+ }
+ return delegate.readObject(str, type);
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
+ }
}
@Override
public <T> T fromJson(final String str, final Type runtimeType) throws JsonbException {
- if (isArray(runtimeType)) {
- return (T) delegate.readArray(new StringReader(str), Class.class.cast(runtimeType).getComponentType());
- } else if (isCollection(runtimeType)) {
- return (T) delegate.readCollection(new StringReader(str), ParameterizedType.class.cast(runtimeType));
+ try {
+ if (isArray(runtimeType)) {
+ final Class cast = Class.class.cast(runtimeType);
+ return (T) delegate.readTypedArray(new StringReader(str), cast.getComponentType(), cast);
+ } else if (isCollection(runtimeType)) {
+ return (T) delegate.readCollection(new StringReader(str), ParameterizedType.class.cast(runtimeType));
+ }
+ return delegate.readObject(str, runtimeType);
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
}
- return delegate.readObject(str, runtimeType);
}
@Override
public <T> T fromJson(final Readable readable, final Class<T> type) throws JsonbException {
- return delegate.readObject(toReader(readable), type);
+ try {
+ if (isArray(type)) {
+ return delegate.readTypedArray(toReader(readable), type.getComponentType(), type);
+ } else if (Collection.class.isAssignableFrom(type)) {
+ return (T) delegate.readCollection(toReader(readable), new JohnzonParameterizedType(type, Object.class));
+ }
+ return delegate.readObject(toReader(readable), type);
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
+ }
}
@Override
public <T> T fromJson(final Readable readable, final Type runtimeType) throws JsonbException {
if (isArray(runtimeType)) {
- return (T) delegate.readArray(toReader(readable), Class.class.cast(runtimeType).getComponentType());
+ final Class<T> type = Class.class.cast(runtimeType);
+ return delegate.readTypedArray(toReader(readable), type.getComponentType(), type);
} else if (isCollection(runtimeType)) {
return (T) delegate.readCollection(toReader(readable), ParameterizedType.class.cast(runtimeType));
}
@@ -74,27 +101,97 @@ public class JohnsonJsonb implements Jsonb {
@Override
public <T> T fromJson(final InputStream stream, final Class<T> type) throws JsonbException {
- return delegate.readObject(stream, type);
+ try {
+ if (isArray(type)) {
+ return delegate.readTypedArray(stream, type.getComponentType(), type);
+ } else if (Collection.class.isAssignableFrom(type)) {
+ return (T) delegate.readCollection(stream, new JohnzonParameterizedType(type, Object.class));
+ }
+ return delegate.readObject(stream, type);
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
+ }
}
@Override
public <T> T fromJson(final InputStream stream, final Type runtimeType) throws JsonbException {
- if (isArray(runtimeType)) {
- return (T) delegate.readArray(stream, Class.class.cast(runtimeType).getComponentType());
- } else if (isCollection(runtimeType)) {
- return (T) delegate.readCollection(stream, ParameterizedType.class.cast(runtimeType));
+ try {
+ if (isArray(runtimeType)) {
+ final Class<T> type = Class.class.cast(runtimeType);
+ return delegate.readTypedArray(stream, type.getComponentType(), type);
+ } else if (isCollection(runtimeType)) {
+ return (T) delegate.readCollection(stream, ParameterizedType.class.cast(runtimeType));
+ }
+ return delegate.readObject(stream, runtimeType);
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
}
- return delegate.readObject(stream, runtimeType);
}
@Override
public String toJson(final Object object) throws JsonbException {
- if (isArray(object.getClass())) {
- return delegate.writeArrayAsString((Object[]) object);
- } else if (Collection.class.isInstance(object)) {
- return delegate.writeArrayAsString(Collection.class.cast(object));
+ try {
+ if (isArray(object.getClass())) {
+ return delegate.writeArrayAsString(toArray(object));
+ } else if (Collection.class.isInstance(object)) {
+ return delegate.writeArrayAsString(Collection.class.cast(object));
+ }
+ return delegate.writeObjectAsString(object);
+
+ } catch (final MapperException me) {
+ throw new JsonbException(me.getMessage(), me);
}
- return delegate.writeObjectAsString(object);
+ }
+
+ private Object[] toArray(final Object object) {
+ final Class<?> componentType = object.getClass().getComponentType();
+ Object[] array;
+ if (int.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getInt(object, i);
+ }
+ } else if (double.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getDouble(object, i);
+ }
+ } else if (byte.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getByte(object, i);
+ }
+ } else if (char.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getChar(object, i);
+ }
+ } else if (float.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getFloat(object, i);
+ }
+ } else if (long.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getLong(object, i);
+ }
+ } else if (short.class == componentType) {
+ final int length = Array.getLength(object);
+ array = new Integer[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = Array.getShort(object, i);
+ }
+ } else {
+ array = (Object[]) object;
+ }
+ return array;
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/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 abb74b2..29a26fb 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
@@ -36,6 +36,7 @@ import javax.json.spi.JsonProvider;
import javax.json.stream.JsonGenerator;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
@@ -57,12 +58,14 @@ import java.util.Map;
import java.util.Optional;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
import static java.util.Collections.emptyMap;
import static java.util.Optional.ofNullable;
import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY;
-import static javax.json.bind.config.PropertyOrderStrategy.ANY;
+import static javax.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL;
public class JohnzonBuilder implements JsonbBuilder {
private final MapperBuilder builder = new MapperBuilder();
@@ -102,17 +105,21 @@ public class JohnzonBuilder implements JsonbBuilder {
final Optional<Object> namingStrategyValue = config.getProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY);
final PropertyNamingStrategy propertyNamingStrategy = new PropertyNamingStrategyFactory(namingStrategyValue.orElse(IDENTITY)).create();
- final String orderValue = config.getProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY).map(String::valueOf).orElse(ANY);
+ 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<>();
+
@Override
public boolean isVisible(final Field field) {
- 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) {
- return true;
+ 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
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
----------------------------------------------------------------------
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 eda5fe2..854cae2 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
@@ -71,6 +71,7 @@ import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
import static org.apache.johnzon.mapper.reflection.Converters.matches;
public class JsonbAccessMode implements AccessMode {
@@ -88,11 +89,17 @@ public class JsonbAccessMode implements AccessMode {
this.order = orderValue;
this.visibility = visibilityStrategy;
this.caseSensitive = caseSensitive;
- this.delegate = new FieldAndMethodAccessMode(true, false);
+ this.delegate = new FieldAndMethodAccessMode(true, true);
this.defaultConverters = defaultConverters;
}
@Override
+ public Comparator<String> fieldComparator(final Class<?> clazz) {
+ final Comparator<String> orderComparator = orderComparator(clazz);
+ return caseSensitive ? orderComparator : ((o1, o2) -> o1.equalsIgnoreCase(o2) ? 0 : orderComparator.compare(o1, o2));
+ }
+
+ @Override
public Factory findFactory(final Class<?> clazz) {
Constructor<?> constructor = null;
Method factory = null;
@@ -276,32 +283,41 @@ public class JsonbAccessMode implements AccessMode {
public Map<String, Reader> findReaders(final Class<?> clazz) {
final Map<String, Reader> readers = delegate.findReaders(clazz);
- final Comparator<String> orderComparator = orderComparator(clazz);
- final Comparator<String> keyComparator = caseSensitive ?
- orderComparator :
- (o1, o2) -> o1.equalsIgnoreCase(o2) ? 0 : orderComparator.compare(o1, o2);
-
+ final Comparator<String> keyComparator = fieldComparator(clazz);
final Map<String, Reader> result = keyComparator == null ? new HashMap<>() : new TreeMap<>(keyComparator);
for (final Map.Entry<String, Reader> entry : readers.entrySet()) {
- final Reader value = entry.getValue();
- if (isTransient(value, visibility)) {
+ final Reader initialReader = entry.getValue();
+ if (isTransient(initialReader, visibility)) {
continue;
}
+ final Reader finalReader;
+ if (FieldAndMethodAccessMode.CompositeDecoratedType.class.isInstance(initialReader)) { // unwrap to use the right reader
+ final FieldAndMethodAccessMode.CompositeDecoratedType decoratedType = FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(initialReader);
+ final DecoratedType type2 = decoratedType.getType2();
+ if (MethodAccessMode.MethodReader.class.isInstance(type2)) {
+ finalReader = Reader.class.cast(type2);
+ } else {
+ finalReader = initialReader;
+ }
+ } else {
+ finalReader = initialReader;
+ }
+
// we are visible
- final JsonbProperty property = value.getAnnotation(JsonbProperty.class);
- final JsonbNillable nillable = value.getClassOrPackageAnnotation(JsonbNillable.class);
+ final JsonbProperty property = initialReader.getAnnotation(JsonbProperty.class);
+ final JsonbNillable nillable = initialReader.getClassOrPackageAnnotation(JsonbNillable.class);
final boolean isNillable = nillable != null || (property != null && property.nillable());
- final JsonbTypeAdapter adapter = value.getAnnotation(JsonbTypeAdapter.class);
- final JsonbDateFormat dateFormat = value.getAnnotation(JsonbDateFormat.class);
- final JsonbNumberFormat numberFormat = value.getAnnotation(JsonbNumberFormat.class);
- final JsonbValue jsonbValue = value.getAnnotation(JsonbValue.class);
- validateAnnotations(value, adapter, dateFormat, numberFormat, jsonbValue);
+ final JsonbTypeAdapter adapter = initialReader.getAnnotation(JsonbTypeAdapter.class);
+ final JsonbDateFormat dateFormat = initialReader.getAnnotation(JsonbDateFormat.class);
+ final JsonbNumberFormat numberFormat = initialReader.getAnnotation(JsonbNumberFormat.class);
+ final JsonbValue jsonbValue = initialReader.getAnnotation(JsonbValue.class);
+ validateAnnotations(initialReader, adapter, dateFormat, numberFormat, jsonbValue);
final Converter<?> converter;
try {
- converter = adapter == null && dateFormat == null && numberFormat == null && jsonbValue == null ? defaultConverters.get(value.getType()) :
- toConverter(value.getType(), adapter, dateFormat, numberFormat);
+ converter = adapter == null && dateFormat == null && numberFormat == null && jsonbValue == null ? defaultConverters.get(initialReader.getType()) :
+ toConverter(initialReader.getType(), adapter, dateFormat, numberFormat);
} catch (final InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
@@ -309,21 +325,21 @@ public class JsonbAccessMode implements AccessMode {
// handle optionals since mapper is still only java 7
final Type type;
final Function<Object, Object> reader;
- if (isOptional(value)) {
- type = ParameterizedType.class.cast(value.getType()).getActualTypeArguments()[0];
- reader = i -> Optional.class.cast(value.read(i)).orElse(null);
- } else if (OptionalInt.class == value.getType()) {
+ if (isOptional(finalReader)) {
+ type = ParameterizedType.class.cast(finalReader.getType()).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(value.read(i)).orElse(0);
- } else if (OptionalLong.class == value.getType()) {
+ reader = i -> OptionalInt.class.cast(finalReader.read(i)).orElse(0);
+ } else if (OptionalLong.class == finalReader.getType()) {
type = long.class;
- reader = i -> OptionalLong.class.cast(value.read(i)).orElse(0);
- } else if (OptionalDouble.class == value.getType()) {
+ reader = i -> OptionalLong.class.cast(finalReader.read(i)).orElse(0);
+ } else if (OptionalDouble.class == finalReader.getType()) {
type = double.class;
- reader = i -> OptionalDouble.class.cast(value.read(i)).orElse(0);
+ reader = i -> OptionalDouble.class.cast(finalReader.read(i)).orElse(0);
} else {
- type = value.getType();
- reader = value::read;
+ type = finalReader.getType();
+ reader = finalReader::read;
}
final String key = property == null || property.value().isEmpty() ? naming.translateName(entry.getKey()) : property.value();
@@ -340,12 +356,12 @@ public class JsonbAccessMode implements AccessMode {
@Override
public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
- return value.getAnnotation(clazz);
+ return finalReader.getAnnotation(clazz);
}
@Override
public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
- return value.getClassOrPackageAnnotation(clazz);
+ return finalReader.getClassOrPackageAnnotation(clazz);
}
@Override
@@ -368,28 +384,41 @@ public class JsonbAccessMode implements AccessMode {
public Map<String, Writer> findWriters(final Class<?> clazz) {
final Map<String, Writer> writers = delegate.findWriters(clazz);
- final Comparator<String> keyComparator = orderComparator(clazz);
+ final Comparator<String> keyComparator = fieldComparator(clazz);
final Map<String, Writer> result = keyComparator == null ? new HashMap<>() : new TreeMap<>(keyComparator);
for (final Map.Entry<String, Writer> entry : writers.entrySet()) {
- final Writer value = entry.getValue();
- if (isTransient(value, visibility)) {
+ Writer initialWriter = entry.getValue();
+ if (isTransient(initialWriter, visibility)) {
continue;
}
+ final Writer finalWriter;
+ if (FieldAndMethodAccessMode.CompositeDecoratedType.class.isInstance(initialWriter)) { // unwrap to use the right reader
+ final FieldAndMethodAccessMode.CompositeDecoratedType decoratedType = FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(initialWriter);
+ final DecoratedType type2 = decoratedType.getType2();
+ if (MethodAccessMode.MethodWriter.class.isInstance(type2)) {
+ finalWriter = Writer.class.cast(type2);
+ } else {
+ finalWriter = initialWriter;
+ }
+ } else {
+ finalWriter = initialWriter;
+ }
+
// we are visible
- final JsonbProperty property = value.getAnnotation(JsonbProperty.class);
- final JsonbNillable nillable = value.getClassOrPackageAnnotation(JsonbNillable.class);
+ final JsonbProperty property = initialWriter.getAnnotation(JsonbProperty.class);
+ final JsonbNillable nillable = initialWriter.getClassOrPackageAnnotation(JsonbNillable.class);
final boolean isNillable = nillable != null || (property != null && property.nillable());
- final JsonbTypeAdapter adapter = value.getAnnotation(JsonbTypeAdapter.class);
- final JsonbDateFormat dateFormat = value.getAnnotation(JsonbDateFormat.class);
- final JsonbNumberFormat numberFormat = value.getAnnotation(JsonbNumberFormat.class);
- final JsonbValue jsonbValue = value.getAnnotation(JsonbValue.class);
- validateAnnotations(value, adapter, dateFormat, numberFormat, jsonbValue);
+ final JsonbTypeAdapter adapter = initialWriter.getAnnotation(JsonbTypeAdapter.class);
+ final JsonbDateFormat dateFormat = initialWriter.getAnnotation(JsonbDateFormat.class);
+ final JsonbNumberFormat numberFormat = initialWriter.getAnnotation(JsonbNumberFormat.class);
+ final JsonbValue jsonbValue = initialWriter.getAnnotation(JsonbValue.class);
+ validateAnnotations(initialWriter, adapter, dateFormat, numberFormat, jsonbValue);
final Converter<?> converter;
try {
- converter = adapter == null && dateFormat == null && numberFormat == null && jsonbValue == null ? defaultConverters.get(value.getType()) :
- toConverter(value.getType(), adapter, dateFormat, numberFormat);
+ converter = adapter == null && dateFormat == null && numberFormat == null && jsonbValue == null ? defaultConverters.get(initialWriter.getType()) :
+ toConverter(initialWriter.getType(), adapter, dateFormat, numberFormat);
} catch (final InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
@@ -397,21 +426,21 @@ public class JsonbAccessMode implements AccessMode {
// handle optionals since mapper is still only java 7
final Type type;
final BiConsumer<Object, Object> writer;
- if (isOptional(value)) {
- type = ParameterizedType.class.cast(value.getType()).getActualTypeArguments()[0];
- writer = (i, val) -> value.write(i, Optional.ofNullable(val));
- } else if (OptionalInt.class == value.getType()) {
+ if (isOptional(initialWriter)) {
+ type = ParameterizedType.class.cast(initialWriter.getType()).getActualTypeArguments()[0];
+ writer = (i, val) -> finalWriter.write(i, Optional.ofNullable(val));
+ } else if (OptionalInt.class == initialWriter.getType()) {
type = int.class;
- writer = (i, val) -> value.write(i, OptionalInt.of(Number.class.cast(val).intValue()));
- } else if (OptionalLong.class == value.getType()) {
+ 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) -> value.write(i, OptionalLong.of(Number.class.cast(val).longValue()));
- } else if (OptionalDouble.class == value.getType()) {
+ 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) -> value.write(i, OptionalDouble.of(Number.class.cast(val).doubleValue()));
+ writer = (i, val) -> finalWriter.write(i, OptionalDouble.of(Number.class.cast(val).doubleValue()));
} else {
- type = value.getType();
- writer = value::write;
+ type = initialWriter.getType();
+ writer = finalWriter::write;
}
final String key = property == null || property.value().isEmpty() ? naming.translateName(entry.getKey()) : property.value();
@@ -428,12 +457,12 @@ public class JsonbAccessMode implements AccessMode {
@Override
public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
- return value.getAnnotation(clazz);
+ return initialWriter.getAnnotation(clazz);
}
@Override
public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
- return value.getClassOrPackageAnnotation(clazz);
+ return initialWriter.getClassOrPackageAnnotation(clazz);
}
@Override
@@ -457,9 +486,9 @@ public class JsonbAccessMode implements AccessMode {
}
private boolean isTransient(final DecoratedType dt, final PropertyVisibilityStrategy visibility) {
- return shouldSkip(visibility, dt) ||
- (FieldAndMethodAccessMode.CompositeDecoratedType.class.isInstance(dt) &&
- Stream.of(FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(dt).getType1(), FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(dt).getType2())
+ return shouldSkip(visibility, dt) &&
+ (!FieldAndMethodAccessMode.CompositeDecoratedType.class.isInstance(dt) ||
+ !Stream.of(FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(dt).getType1(), FieldAndMethodAccessMode.CompositeDecoratedType.class.cast(dt).getType2())
.map(t -> shouldSkip(visibility, t))
.filter(a -> a)
.findAny()
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java
----------------------------------------------------------------------
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java
new file mode 100644
index 0000000..518f8c8
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultMappingTest.java
@@ -0,0 +1,1312 @@
+/*
+ * 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;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+// taken from the examples of the spec
+// TODO: bunch of asserts
+//CHECKSTYLE:OFF
+public class DefaultMappingTest {
+ private static final Jsonb JSONB = JsonbBuilder.create();
+
+ @Test
+ @Ignore("should it be supported")
+ public void primitives() throws Exception {
+ fromJsonPrimitives();
+ toJsonPrimitives();
+ fromJsonURLURI();
+ toJsonURLURI();
+ }
+
+ @Test
+ public void naming() throws Exception {
+ toJsonDefaultNames();
+ fromJsonDefaultNames();
+ }
+
+ @Test
+ public void collections() {
+ fromJsonCollections();
+ toJsonCollection();
+ }
+
+ @Test
+ public void arrays() {
+ fromJsonArrays();
+ toJsonArrays();
+ }
+
+ @Test
+ public void structures() throws Exception {
+ fromJsonStructures();
+ toJsonStructures();
+ }
+
+ @Test
+ public void pojos() throws Exception {
+ fromJsonPOJOs();
+ toJsonPOJOs();
+ }
+
+ @Test
+ public void inheritance() throws Exception {
+ fromJsonInheritance();
+ toJsonInheritance();
+ }
+
+ @Test
+ public void anonymous() throws Exception {
+ toJsonAnonymousClass();
+ }
+
+ @Test
+ public void instantiation() throws Exception {
+ fromJsonInstantiation();
+ }
+
+ @Test
+ public void order() throws Exception {
+ toJsonAttributesOrdering();
+ }
+
+ @Test
+ public void nulls() throws Exception {
+ toJsonNullValues();
+ fromJsonNullValues();
+ }
+
+ @Test
+ public void modifiers() throws Exception {
+ toJsonModifiers();
+ fromJsonModifiers();
+ }
+
+ @Test
+ public void optionals() throws Exception {
+ toJsonOptional();
+ fromJsonOptional();
+ }
+
+ @Test
+ public void accessors() throws Exception {
+ toJsonAccessors();
+ fromJsonAccessors();
+ }
+
+ public static void fromJsonPrimitives() {
+ //String
+ String str = JSONB.fromJson("\"some_string\"", String.class);
+
+ //String escaping
+ String escapedString = JSONB.fromJson("\" \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u0039\"", String.class);
+ assertEquals(" \" \\ / \b \f \n \r \t 9", escapedString);
+
+ //Character
+ Character ch = JSONB.fromJson("\"\uFFFF\"", Character.class);
+
+ //Byte
+ Byte byte1 = JSONB.fromJson("1", Byte.class);
+
+ //Short
+ Short short1 = JSONB.fromJson("1", Short.class);
+
+ //Integer
+ Integer int1 = JSONB.fromJson("1", Integer.class);
+
+ //Long
+ Long long1 = JSONB.fromJson("1", Long.class);
+
+ //Float
+ Float float1 = JSONB.fromJson("1.2", Float.class);
+
+ //Double
+ Double double1 = JSONB.fromJson("1.2", Double.class);
+
+ //BigInteger
+ BigInteger bigInteger = JSONB.fromJson("1", BigInteger.class);
+
+ //BigDecimal
+ BigDecimal bigDecimal = JSONB.fromJson("1.2", BigDecimal.class);
+
+ //Number
+ Number number = JSONB.fromJson("1.2", Number.class);
+
+ //Boolean
+ Boolean trueValue = JSONB.fromJson("true", Boolean.class);
+
+ //Boolean
+ Boolean falseValue = JSONB.fromJson("false", Boolean.class);
+
+ //null
+ Object nullValue = JSONB.fromJson("null", Object.class);
+
+ assertTrue(nullValue == null);
+ }
+
+ public static void exceptions() {
+ //Exception
+ //fail fast strategy by default
+
+ //incompatible types
+ try {
+ JSONB.fromJson("not_a_number", Integer.class);
+ assertTrue(false);
+ } catch (JsonbException e) {
+ }
+
+ //incompatible types
+ try {
+ JSONB.fromJson("[null,1]", int[].class);
+ assertTrue(false);
+ } catch (JsonbException e) {
+ }
+
+ //bad structure
+ try {
+ JSONB.fromJson("[1,2", int[].class);
+ assertTrue(false);
+ } catch (JsonbException e) {
+ }
+
+ //overflow - Value out of range
+ try {
+ JSONB.fromJson("" + new Integer(Byte.MAX_VALUE + 1) + "", Byte.class);
+ assertTrue(false);
+ } catch (JsonbException e) {
+ }
+
+ //underflow - Value out of range
+ try {
+ JSONB.fromJson("" + new Integer(Byte.MIN_VALUE - 1) + "", Byte.class);
+ assertTrue(false);
+ } catch (JsonbException e) {
+ }
+ }
+
+ public static void toJsonPrimitives() {
+
+ //String
+ assertEquals("\"some_string\"", JSONB.toJson("some_string"));
+
+ //escaped String
+ assertEquals("\" \\\\ \\\" / \\b \\f \\n \\r \\t 9\"", JSONB.toJson(" \\ \" / \b \f \n \r \t \u0039"));
+
+ //Character
+ assertEquals("\"\uFFFF\"", JSONB.toJson('\uFFFF'));
+
+ //Byte
+ assertEquals("1", JSONB.toJson((byte) 1));
+
+ //Short
+ assertEquals("1", JSONB.toJson((short) 1));
+
+ //Integer
+ assertEquals("1", JSONB.toJson(1));
+
+ //Long
+ assertEquals("5", JSONB.toJson(5L));
+
+ //Float
+ assertEquals("1.2", JSONB.toJson(1.2f));
+
+ //Double
+ assertEquals("1.2", JSONB.toJson(1.2));
+
+ //BigInteger
+ assertEquals("1", JSONB.toJson(new BigInteger("1")));
+
+ //BigDecimal
+ assertEquals("1.2", JSONB.toJson(new BigDecimal("1.2")));
+
+ //Number
+ assertEquals("1.2", JSONB.toJson((java.lang.Number) 1.2));
+
+ //Boolean true
+ assertEquals("true", JSONB.toJson(true));
+
+ //Boolean false
+ assertEquals("false", JSONB.toJson(false));
+
+ //null
+ assertEquals("null", JSONB.toJson(null));
+ }
+
+ public static void fromJsonStructures() {
+
+ //Map
+ Map<String, Object> map = (Map<String, Object>) JSONB.fromJson("{\"name\":\"unknown object\"}", Object.class);
+
+ //mapping for number -> Integer, Long, BigDecimal
+ Map<String, Object> mapWithBigDecimal = (Map<String, Object>) JSONB.fromJson("{\"intValue\":5,\"longValue\":17179869184,\"otherValue\":1.2}", Object.class);
+ assertTrue(mapWithBigDecimal.get("intValue") instanceof Integer);
+ assertTrue(mapWithBigDecimal.get("longValue") instanceof Long);
+ assertTrue(mapWithBigDecimal.get("otherValue") instanceof BigDecimal);
+
+ //Collection
+ /* why collection and not array or sthg else?
+ Collection<Object> collection = (Collection<Object>) JSONB.fromJson("[{\"value\":\"first\"}, {\"value\":\"second\"}]", Object.class);
+ */
+
+ //JsonStructure
+ assertNotNull(JSONB.fromJson("{\"name\":\"unknown object\"}", JsonStructure.class));
+
+ //JsonObject
+ assertNotNull(JSONB.fromJson("{\"name\":\"unknown object\"}", JsonObject.class));
+
+ //JsonArray
+ assertNotNull(JSONB.fromJson("[{\"value\":\"first\"},{\"value\":\"second\"}]", JsonArray.class));
+
+ //JsonValue
+ // TBD assertNotNull(JSONB.fromJson("1", JsonValue.class));
+ }
+
+ public static void toJsonStructures() {
+
+ JsonBuilderFactory factory = Json.createBuilderFactory(Collections.emptyMap());
+ JsonObject jsonObject = factory.createObjectBuilder().
+ add("name", "home").
+ add("city", "Prague")
+ .build();
+
+ //JsonObject
+ assertEquals("{\"name\":\"home\",\"city\":\"Prague\"}", JSONB.toJson(jsonObject));
+
+ JsonArray jsonArray = factory.createArrayBuilder().add(jsonObject).add(jsonObject).build();
+
+ //JsonArray
+ assertEquals("[{\"name\":\"home\",\"city\":\"Prague\"},{\"name\":\"home\",\"city\":\"Prague\"}]", JSONB.toJson(jsonArray));
+
+ //JsonStructure
+ assertEquals("[{\"name\":\"home\",\"city\":\"Prague\"},{\"name\":\"home\",\"city\":\"Prague\"}]", JSONB.toJson((JsonStructure) jsonArray));
+
+ //JsonValue
+ assertEquals("true", JSONB.toJson(JsonValue.TRUE));
+
+ //Map
+ Map<String, Object> commonMap = new LinkedHashMap<>();
+ commonMap.put("first", 1);
+ commonMap.put("second", 2);
+
+ assertEquals("{\"first\":1,\"second\":2}", JSONB.toJson(commonMap));
+
+ //Collection
+ Collection<Object> commonList = new ArrayList<>();
+ commonList.add(1);
+ commonList.add(2);
+
+ assertEquals("[1,2]", JSONB.toJson(commonList));
+ }
+
+ public static void fromJsonCollections() {
+
+ //support deserialization of java.util.Collection and java.util.Map and its subinterfaces and implementing (sub)classes
+
+ //Collection, Map
+
+ //Set, HashSet, NavigableSet, SortedSet, TreeSet, LinkedHashSet, TreeHashSet
+
+ //HashMap, NavigableMap, SortedMap, TreeMap, LinkedHashMap, TreeHashMap
+
+ //List, ArrayList, LinkedList
+
+ //Deque, ArrayDeque, Queue, PriorityQueue
+
+ Collection<Object> collection = JSONB.fromJson("[\"first\",\"second\"]", Collection.class);
+
+ Map<String, Object> map = JSONB.fromJson("{\"first\":\"second\"}", Map.class);
+
+ //concrete implementation of Map
+ HashMap<String, Object> hashMap = JSONB.fromJson("{\"first\":\"second\"}", HashMap.class);
+
+ //concrete implementation of Collection
+ ArrayList<Object> arrayList = JSONB.fromJson("[\"first\",\"second\"]", ArrayList.class);
+
+ //deque
+ Deque<String> dequeList = JSONB.fromJson("[\"first\",\"second\"]", Deque.class);
+ assertEquals(2, dequeList.size());
+ assertEquals("first", dequeList.getFirst());
+ assertEquals("second", dequeList.getLast());
+
+ //JSON Binding supports default deserialization of the following interfaces
+ //syntax: interface -> default implementation
+
+ //Collection -> ArrayList
+ //Set -> HashSet
+ //NavigableSet -> TreeSet
+ //SortedSet -> TreeSet
+ //Map -> HashMap
+ //SortedMap -> TreeMap
+ //NavigableMap -> TreeMap
+ //Deque -> ArrayDeque
+ //Queue -> ArrayDeque
+
+ //any implementation of Collection and Map with public default constructor is deserializable
+
+ }
+
+ public static void toJsonCollection() {
+ Collection<Integer> collection = Arrays.asList(1, 2, 3);
+
+ assertEquals("[1,2,3]", JSONB.toJson(collection));
+
+ Map<String, Integer> map = new LinkedHashMap<>();
+ map.put("1", 1);
+ map.put("2", 2);
+ map.put("3", 3);
+
+ assertEquals("{\"1\":1,\"2\":2,\"3\":3}", JSONB.toJson(map));
+
+ //any implementation of Collection and Map is serializable
+
+ //deque
+ Deque<String> deque = new ArrayDeque<>();
+ deque.add("first");
+ deque.add("second");
+
+ assertEquals("[\"first\",\"second\"]", JSONB.toJson(deque));
+ }
+
+ public static void fromJsonArrays() {
+
+ //support of arrays of types that JSON Binding is able to deserialize
+ //Byte[], Short[], Integer[] Long[], Float[], Double[], BigInteger[], BigDecimal[], Number[]
+ //Object[], JsonArray[], JsonObject[], JsonStructure[]
+ //String[], Character[]
+ //byte[], short[], int[], long[], float[], double[], char[], boolean[]
+ //java.net.URL[], java.net.URI[]
+ //Map[], Collection[], other collections ...
+ //enum, EnumSet, EnumMap
+ //support of multidimensional arrays
+
+
+ //Several examples
+
+ //Byte arrays
+ Byte[] byteArray = JSONB.fromJson("[1,2]", Byte[].class);
+
+ //Integer array
+ Integer[] integerArray = JSONB.fromJson("[1,2]", Integer[].class);
+
+ //int array
+ int[] intArray = JSONB.fromJson("[1,2]", int[].class);
+
+ //String arrays
+ String[] stringArray = JSONB.fromJson("[\"first\",\"second\"]", String[].class);
+
+ //multidimensional arrays
+ String[][] stringMultiArray = JSONB.fromJson("[[\"first\", \"second\"], [\"third\" , \"fourth\"]]", String[][].class);
+
+ //default mapping should handle multidimensional arrays of types supported by default mapping, e.g. Map
+ Map<String, Object>[][] mapMultiArray = JSONB.fromJson("[[{\"1\":2}, {\"3\":4}],[{\"5\":6},{\"7\":8}]]", Map[][].class);
+ }
+
+ public static void toJsonArrays() {
+
+ //support of arrays of types that JSON Binding is able to serialize
+ //Byte[], Short[], Integer[] Long[], Float[], Double[], BigInteger[], BigDecimal[], Number[]
+ //Object[], JsonArray[], JsonObject[], JsonStructure[]
+ //String[], Character[]
+ //byte[], short[], int[], long[], float[], double[], char[], boolean[]
+ //java.net.URL[], java.net.URI[]
+ //Map[], Collection[], other collections ...
+ //enum, EnumSet, EnumMap
+ //support of multidimensional arrays
+
+
+ //Several examples
+
+ Byte[] byteArray = {1, 2, 3};
+
+ assertEquals("[1,2,3]", JSONB.toJson(byteArray));
+
+ Integer[] integerArray = {1, 2, 3};
+
+ assertEquals("[1,2,3]", JSONB.toJson(integerArray));
+
+ int[] intArray = {1, 2, 3};
+
+ assertEquals("[1,2,3]", JSONB.toJson(intArray));
+
+ String[] stringArray = {"first", "second", "third"};
+
+ assertEquals("[\"first\",\"second\",\"third\"]", JSONB.toJson(stringArray));
+
+ String[][] stringMultiArray = {{"first", "second"}, {"third", "fourth"}};
+
+ assertEquals("[[\"first\",\"second\"],[\"third\",\"fourth\"]]", JSONB.toJson(stringMultiArray));
+
+ Map<String, Object>[][] mapMultiArray = new LinkedHashMap[2][2];
+
+ mapMultiArray[0][0] = new LinkedHashMap<>(1);
+ mapMultiArray[0][0].put("0", 0);
+ mapMultiArray[0][1] = new LinkedHashMap<>(1);
+ mapMultiArray[0][1].put("0", 1);
+ mapMultiArray[1][0] = new LinkedHashMap<>(1);
+ mapMultiArray[1][0].put("1", 0);
+ mapMultiArray[1][1] = new LinkedHashMap<>(1);
+ mapMultiArray[1][1].put("1", 1);
+
+ assertEquals("[[{\"0\":0},{\"0\":1}],[{\"1\":0},{\"1\":1}]]", JSONB.toJson(mapMultiArray));
+ }
+
+ public EnumSet<Language> languageEnumSet = EnumSet.of(Language.Czech);
+ public EnumMap<Language, String> languageEnumMap = new EnumMap<>(Language.class);
+
+ public static void fromJsonEnums() throws Exception {
+
+ EnumSet<Language> languageEnumSet = JSONB.fromJson("[\"Slovak\", \"English\"]", DefaultMappingTest.class.getField("languageEnumSet").getGenericType());
+
+ EnumMap<Language, String> languageEnumMap = JSONB.fromJson("[\"Slovak\" : \"sk\", \"Czech\" : \"cz\"]", DefaultMappingTest.class.getField("languageEnumMap").getGenericType());
+ }
+
+ public static void toJsonEnums() {
+
+ Language language = Language.Slovak;
+
+ assertEquals("\"Slovak\"", JSONB.toJson(language));
+
+ EnumSet<Language> languageEnumSet = EnumSet.of(Language.Czech, Language.Slovak);
+
+ assertEquals("\"Czech\",\"Slovak\"", JSONB.toJson(languageEnumSet));
+
+ EnumMap<Language, String> languageEnumMap = new EnumMap<>(Language.class);
+ languageEnumMap.put(Language.Czech, "cz");
+ languageEnumMap.put(Language.English, "en");
+
+ assertEquals("{\"Czech\":\"cz\",\"English\":\"en\"}", languageEnumMap);
+ }
+
+ private enum Language {
+ English, Slovak, Czech
+ }
+
+ public static void fromJsonPOJOs() {
+
+ POJO pojo = JSONB.fromJson("{\"id\":1, \"name\":\"pojoName\"}", POJO.class);
+
+ POJO nullPOJO = JSONB.fromJson("{\"id\":1, \"name\":null}", POJO.class);
+ assertTrue(null == nullPOJO.name);
+
+ //just public nested class
+ try {
+ POJOWithNestedClass pojoWithNestedClass = JSONB.fromJson("{\"id\":1, \"name\":\"pojo_name\", \"nestedClass\" : {\"nestedId\":2, \"nestedName\" : \"nestedPojoName\"}}", POJOWithNestedClass.class);
+ fail();
+ } catch (final JsonbException e) {
+ // ok
+ }
+
+ //just public nested class
+ try {
+ POJOWithNestedClass.NestedClass nestedClass = JSONB.fromJson("{\"nestedId\":2, \"nestedName\" : \"nestedPojoName\"}", POJOWithNestedClass.NestedClass.class);
+ fail();
+ } catch (final JsonbException e) {
+ // ok
+ }
+
+ POJOWithStaticNestedClass pojoWithStaticNestedClass = JSONB.fromJson("{\"id\":1, \"name\":\"pojoName\"}", POJOWithStaticNestedClass.class);
+
+ POJOWithStaticNestedClass.StaticNestedClass staticNestedClass = JSONB.fromJson("{\"nestedId\":2, \"nestedName\" : \"nestedPojoName\"}", POJOWithStaticNestedClass.StaticNestedClass.class);
+
+ POJOWithMixedFieldAccess pojoWithMixedFieldAccess =
+ JSONB.fromJson("{\"id\":5, \"name\":\"new_name\", \"active\":true}"/*, \"valid\":true}"*/, POJOWithMixedFieldAccess.class);
+
+ assertTrue(pojoWithMixedFieldAccess.id.intValue() == 10);
+ assertTrue(pojoWithMixedFieldAccess.name.equals("new_name"));
+ assertTrue(pojoWithMixedFieldAccess.active);
+ assertNull(pojoWithMixedFieldAccess.valid);
+
+ //composite class
+ CompositePOJO compositePOJO = JSONB.fromJson(
+ "{\"compositeId\":\"13\"," +
+ "\"stringArray\":[\"first\",\"second\"],\"stringList\":[\"one\",\"two\"]}", CompositePOJO.class);
+ }
+
+ public static void toJsonPOJOs() {
+
+ POJO pojo = new POJO();
+ pojo.setId(1);
+ pojo.setName("pojoName");
+
+ assertEquals("{\"id\":1,\"name\":\"pojoName\"}", JSONB.toJson(pojo));
+
+ //pojo with nested class
+ POJOWithNestedClass pojoWithNestedClass = new POJOWithNestedClass();
+ pojoWithNestedClass.setName("pojoName");
+ pojoWithNestedClass.setId(1);
+
+ POJOWithNestedClass.NestedClass nestedClass = pojoWithNestedClass.new NestedClass();
+ nestedClass.setNestedId(2);
+ nestedClass.setNestedName("nestedPojoName");
+
+ pojoWithNestedClass.setNestedClass(nestedClass);
+
+ assertEquals("{\"id\":1,\"name\":\"pojoName\",\"nestedClass\":{\"nestedId\":2,\"nestedName\":\"nestedPojoName\"}}", JSONB.toJson(pojoWithNestedClass));
+
+ //nested class
+ assertEquals("{\"nestedId\":2,\"nestedName\":\"nestedPojoName\"}", JSONB.toJson(nestedClass));
+
+ //pojo with static nested class
+ POJOWithStaticNestedClass pojoWithStaticNestedClass = new POJOWithStaticNestedClass();
+ pojoWithStaticNestedClass.setId(1);
+ pojoWithStaticNestedClass.setName("pojoName");
+
+ assertEquals("{\"id\":1,\"name\":\"pojoName\"}", JSONB.toJson(pojoWithStaticNestedClass));
+
+ //static nested class
+ POJOWithStaticNestedClass.StaticNestedClass staticNestedClass = new POJOWithStaticNestedClass.StaticNestedClass();
+ staticNestedClass.setNestedId(2);
+ staticNestedClass.setNestedName("nestedPojoName");
+
+ assertEquals("{\"nestedId\":2,\"nestedName\":\"nestedPojoName\"}", JSONB.toJson(staticNestedClass));
+
+ POJOWithMixedFieldAccess pojoWithMixedFieldAccess = new POJOWithMixedFieldAccess();
+
+ assertEquals("{\"active\":true,\"id\":2,\"name\":\"pojoName\"}"/*,\"valid\":false}"*/, JSONB.toJson(pojoWithMixedFieldAccess));
+
+ //composite class
+ CompositePOJO compositePOJO = new CompositePOJO();
+ compositePOJO.setCompositeId(13);
+ compositePOJO.setStringArray(new String[]{"first", "second"});
+ compositePOJO.setStringList(Arrays.asList("one", "two"));
+ POJO innerPOJO = new POJO();
+ innerPOJO.setId(4);
+ innerPOJO.setName("innerPOJO");
+ compositePOJO.setInner(innerPOJO);
+
+ assertEquals(
+ "{\"compositeId\":13,\"inner\":{\"id\":4,\"name\":\"innerPOJO\"}," +
+ "\"stringArray\":[\"first\",\"second\"],\"stringList\":[\"one\",\"two\"]}",
+ JSONB.toJson(compositePOJO));
+
+ }
+
+ static class CompositePOJO {
+ private Integer compositeId;
+ private POJO inner;
+ private List<String> stringList;
+ private String[] stringArray;
+
+ public CompositePOJO() {
+ }
+
+ public Integer getCompositeId() {
+ return compositeId;
+ }
+
+ public void setCompositeId(Integer compositeId) {
+ this.compositeId = compositeId;
+ }
+
+ public POJO getInner() {
+ return inner;
+ }
+
+ public void setInner(POJO inner) {
+ this.inner = inner;
+ }
+
+ public List<String> getStringList() {
+ return stringList;
+ }
+
+ public void setStringList(List<String> stringList) {
+ this.stringList = stringList;
+ }
+
+ public String[] getStringArray() {
+ return stringArray;
+ }
+
+ public void setStringArray(String[] stringArray) {
+ this.stringArray = stringArray;
+ }
+ }
+
+ private static class POJO {
+ private Integer id;
+ private String name;
+
+ public POJO() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ //other supported attributes
+ }
+
+ private static class POJOWithNestedClass {
+ private Integer id;
+ private String name;
+ private NestedClass nestedClass;
+
+ public POJOWithNestedClass() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public NestedClass getNestedClass() {
+ return nestedClass;
+ }
+
+ public void setNestedClass(NestedClass nestedClass) {
+ this.nestedClass = nestedClass;
+ }
+
+ //other supported attributes
+
+ public class NestedClass {
+ private Integer nestedId;
+ private String nestedName;
+
+ public NestedClass() {
+ }
+
+ public Integer getNestedId() {
+ return nestedId;
+ }
+
+ public void setNestedId(Integer nestedId) {
+ this.nestedId = nestedId;
+ }
+
+ public String getNestedName() {
+ return nestedName;
+ }
+
+ public void setNestedName(String nestedName) {
+ this.nestedName = nestedName;
+ }
+ }
+ }
+
+ private static class POJOWithStaticNestedClass {
+ private Integer id;
+ private String name;
+
+ public POJOWithStaticNestedClass() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ //other supported attributes
+
+ public static class StaticNestedClass {
+ private Integer nestedId;
+ private String nestedName;
+
+ public StaticNestedClass() {
+ }
+
+ public Integer getNestedId() {
+ return nestedId;
+ }
+
+ public void setNestedId(Integer nestedId) {
+ this.nestedId = nestedId;
+ }
+
+ public String getNestedName() {
+ return nestedName;
+ }
+
+ public void setNestedName(String nestedName) {
+ this.nestedName = nestedName;
+ }
+ }
+ }
+
+ private static class POJOWithMixedFieldAccess {
+ public Integer id = 1;
+ public String name = "pojoName";
+ public Boolean active = false;
+ public Boolean valid = null;
+
+ public Integer getId() {
+ return 2;
+ }
+
+ public void setId(Integer id) {
+ this.id = id * 2;
+ }
+
+ public Boolean getActive() {
+ return true;
+ }
+
+ public void setActive(Boolean active) {
+ this.active = active;
+ }
+
+ public Boolean isValid() {
+ return false;
+ }
+
+ public void setValid(Boolean valid) {
+ this.valid = valid;
+ }
+ }
+
+ private static void fromJsonInheritance() {
+ //we need public constructor
+ Dog animal = JSONB.fromJson("{\"age\":5, \"name\":\"Rex\"}", Dog.class);
+ }
+
+ public static void toJsonInheritance() {
+
+ Dog dog = new Dog();
+ dog.setAge(5);
+ dog.setName("Rex");
+
+ assertEquals("{\"age\":5,\"name\":\"Rex\"}", JSONB.toJson(dog), JSONB.toJson(dog));
+ }
+
+ public static class Animal {
+ int age;
+
+ public Animal() {
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+ }
+
+ public static class Dog extends Animal {
+ private String name;
+
+ public Dog() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public static void toJsonAnonymousClass() {
+ assertEquals("{\"id\":1,\"name\":\"pojoName\"}", JSONB.toJson(new POJO() {
+ @Override
+ public Integer getId() {
+ return 1;
+ }
+
+ @Override
+ public String getName() {
+ return "pojoName";
+ }
+ }));
+
+ }
+
+ public static void fromJsonURLURI() {
+ java.net.URL url = JSONB.fromJson("\"https://www.jcp.org/en/jsr/detail?id=367#3\"", java.net.URL.class);
+
+ java.net.URI uri = JSONB.fromJson("\"mailto:users@JSONB-spec.java.net\"", java.net.URI.class);
+ }
+
+ public static void toJsonURLURI() throws Exception {
+
+ java.net.URL url = new java.net.URL("https://www.jcp.org/en/jsr/detail?id=367#3");
+
+ assertEquals("\"https://www.jcp.org/en/jsr/detail?id=367#3\"", JSONB.toJson(url));
+
+ java.net.URI uri = new java.net.URI("mailto:users@JSONB-spec.java.net");
+
+ assertEquals("\"mailto:users@JSONB-spec.java.net\"", JSONB.toJson(uri));
+ }
+
+ static class POJOWithoutDefaultArgConstructor {
+ public String id;
+
+ public POJOWithoutDefaultArgConstructor(String id) {
+ this.id = id;
+ }
+ }
+
+ static class POJOWithPrivateConstructor {
+ public String id;
+
+ private POJOWithPrivateConstructor() {
+ }
+ }
+
+ public static void fromJsonInstantiation() {
+
+ //public or protected constructor must be present
+
+ try {
+ POJOWithoutDefaultArgConstructor pojo = JSONB.fromJson("{\"id\":\"1\"}", POJOWithoutDefaultArgConstructor.class);
+ fail();
+ } catch (JsonbException e) {
+ }
+
+ /* TBD: protected or private is the same
+ try {
+ JSONB.fromJson("{\"id\":\"1\"}", POJOWithPrivateConstructor.class);
+ fail();
+ } catch (JsonbException e) {
+ }
+ */
+ }
+
+ public static void toJsonDefaultNames() {
+ DefaultTestNames defaultTestNames = new DefaultTestNames();
+ String result = JSONB.toJson(defaultTestNames);
+ assertEquals("{\"A\":\"A\",\"ABC\":\"ABC\",\"A_Bc\":\"A_Bc\",\"DdB_ee\":\"DdB_ee\",\"_12ac\":\"_12ac\"," +
+ "\"_23_45_a\":\"_23_45_a\",\"_AB\":\"_AB\",\"_ABc\":\"_ABc\",\"_Ab\":\"_Ab\"," +
+ "\"a\":\"a\",\"a_bC\":\"a_bC\",\"abc\":\"abc\",\"okNotOk\":\"okNotOk\"," +
+ "\"okNot_Ok\":\"okNot_Ok\",\"okNot_ok\":\"okNot_ok\"}", result);
+ }
+
+ public static void fromJsonDefaultNames() {
+ DefaultNames defaultNames = JSONB.fromJson("{\"defaultName\":\"newName\"}", DefaultNames.class);
+ assertEquals("newName", defaultNames.defaultName);
+ assertNull(JSONB.fromJson("{\"defaultNAME\":\"newName\"}", DefaultNames.class).defaultName);
+ }
+
+ static class DefaultNames {
+ public String defaultName;
+
+ public DefaultNames() {
+ }
+ }
+
+ static class DefaultTestNames {
+ public String a = "a";
+ public String A = "A";
+ public String ABC = "ABC";
+ public String abc = "abc";
+ public String a_bC = "a_bC";
+ public String A_Bc = "A_Bc";
+ public String _12ac = "_12ac";
+ public String _Ab = "_Ab";
+ public String _AB = "_AB";
+ public String _ABc = "_ABc";
+ public String okNotOk = "okNotOk";
+ public String okNot_Ok = "okNot_Ok";
+ public String okNot_ok = "okNot_ok";
+ public String DdB_ee = "DdB_ee";
+ public String _23_45_a = "_23_45_a";
+ }
+
+ public static void toJsonAttributesOrdering() {
+ //lexicographical order
+ AttributesOrderingClass attributesOrderingClass = new AttributesOrderingClass();
+ attributesOrderingClass.aField = "text";
+ attributesOrderingClass.cField = "text";
+ attributesOrderingClass.bField = "text";
+
+ assertEquals("{\"aField\":\"text\",\"bField\":\"text\",\"cField\":\"text\"}", JSONB.toJson(attributesOrderingClass));
+
+ AttributesOrderingWithInheritance attributesOrderingWithInheritance = new AttributesOrderingWithInheritance();
+ attributesOrderingWithInheritance.aField = "aField";
+ attributesOrderingWithInheritance.cField = "cField";
+ attributesOrderingWithInheritance.bField = "bField";
+ attributesOrderingWithInheritance.aa = "aa";
+ attributesOrderingWithInheritance.cc = "cc";
+ attributesOrderingWithInheritance.bb = "bb";
+
+ assertEquals("{\"aField\":\"aField\",\"aa\":\"aa\",\"bField\":\"bField\",\"bb\":\"bb\",\"cField\":\"cField\",\"cc\":\"cc\"}",
+ JSONB.toJson(attributesOrderingWithInheritance));
+
+ AttributesOrderingWithCounterClass attributesOrderingWithCounterClass = JSONB.fromJson("{\"second\":\"a\",\"third\":\"b\",\"first\":\"c\"}", AttributesOrderingWithCounterClass.class);
+ assertEquals("a1", attributesOrderingWithCounterClass.second);
+ assertEquals("b2", attributesOrderingWithCounterClass.third);
+ assertEquals("c0", attributesOrderingWithCounterClass.first);
+ }
+
+ public static void toJsonNullValues() {
+ //array
+ List<String> stringList = new ArrayList<>();
+ stringList.add("value1");
+ stringList.add(null);
+ stringList.add("value3");
+
+ assertEquals("[\"value1\",null,\"value3\"]", JSONB.toJson(stringList));
+
+ //java object
+ POJO pojo = new POJO();
+ pojo.id = 1;
+ pojo.name = null;
+
+ assertEquals("{\"id\":1}", JSONB.toJson(pojo));
+ }
+
+ public static void fromJsonNullValues() {
+ //array
+ ArrayList<Object> stringList = JSONB.fromJson("[\"value1\",null,\"value3\"]", ArrayList.class);
+ assertTrue(stringList.size() == 3);
+ Iterator<Object> iterator = stringList.iterator();
+ assertEquals("value1", iterator.next());
+ assertTrue(null == iterator.next());
+ assertEquals("value3", iterator.next());
+
+ //java object
+ POJOWithInitialValue pojoWithInitialValue = JSONB.fromJson("{\"name\":\"newName\"}", POJOWithInitialValue.class);
+ assertTrue(pojoWithInitialValue.id.intValue() == 4);
+ assertEquals("newName", pojoWithInitialValue.name);
+
+ POJOWithInitialValue pojoWithNullValue = JSONB.fromJson("{\"name\":\"newName\",\"id\":null}", POJOWithInitialValue.class);
+ assertTrue(pojoWithNullValue.id == null);
+ assertEquals("newName", pojoWithNullValue.name);
+ }
+
+ public static void toJsonModifiers() {
+ ModifiersClass modifiersClass = new ModifiersClass();
+ assertEquals("{\"finalField\":\"finalValue\",\"regularField\":\"regularValue\"}", JSONB.toJson(modifiersClass));
+ }
+
+ public static void fromJsonModifiers() {
+ //deserialization of final field is ignored
+ ModifiersClass modifiersClass = JSONB.fromJson("{\"finalField\":\"newFinalValue\",\"regularField\":\"newRegularValue\"}", ModifiersClass.class);
+ assertEquals("finalValue", modifiersClass.finalField);
+ assertEquals("newRegularValue", modifiersClass.regularField);
+
+ //deserialization of static field is ignored
+ modifiersClass = JSONB.fromJson("{\"staticField\":\"newStaticValue\",\"regularField\":\"newRegularValue\"}", ModifiersClass.class);
+ assertEquals("staticValue", modifiersClass.staticField);
+ assertEquals("newRegularValue", modifiersClass.regularField);
+
+ //deserialization of transient field is ignored
+ modifiersClass = JSONB.fromJson("{\"transientField\":\"newTransientValue\",\"regularField\":\"newRegularValue\"}", ModifiersClass.class);
+ assertEquals("transientValue", modifiersClass.transientField);
+ assertEquals("newRegularValue", modifiersClass.regularField);
+
+ //deserialization of unknown field is ignored
+ modifiersClass = JSONB.fromJson("{\"unknownField\":\"newUnknownValue\",\"regularField\":\"newRegularValue\"}", ModifiersClass.class);
+ assertEquals("newRegularValue", modifiersClass.regularField);
+ }
+
+ public static void toJsonOptional() {
+
+ //Optional
+ /* TBD: direct mapping
+ assertEquals("\"strValue\"", JSONB.toJson(Optional.of("strValue")));
+
+
+ assertEquals("null", JSONB.toJson(Optional.ofNullable(null)));
+
+ assertEquals("null", JSONB.toJson(Optional.empty()));
+
+ //OptionalInt
+ assertEquals("1", JSONB.toJson(OptionalInt.of(1)));
+ assertEquals("null", JSONB.toJson(OptionalInt.empty()));
+
+ //OptionalLong
+ assertEquals("123", JSONB.toJson(OptionalLong.of(123)));
+ assertEquals("null", JSONB.toJson(OptionalLong.empty()));
+
+ //OptionalDouble
+ assertEquals("1.2", JSONB.toJson(OptionalDouble.of(1.2)));
+ assertEquals("null", JSONB.toJson(OptionalDouble.empty()));
+ */
+
+ final OptionalClass object = new OptionalClass();
+ object.optionalField = Optional.of("test");
+ assertEquals("{\"optionalField\":\"test\"}", JSONB.toJson(object));
+
+ OptionalClass optionalClass = new OptionalClass();
+ optionalClass.optionalField = Optional.of("value");
+
+ assertEquals("{\"optionalField\":\"value\"}", JSONB.toJson(optionalClass));
+
+ OptionalClass nullOptionalField = new OptionalClass();
+ nullOptionalField.optionalField = null;
+
+ assertEquals("{}", JSONB.toJson(nullOptionalField));
+ }
+
+ public static void fromJsonOptional() {
+ /* TBD
+ //Optional
+ Optional<String> stringValue = JSONB.fromJson("\"optionalString\"", Optional.class);
+ assertTrue(stringValue.isPresent());
+ assertEquals("optionalString", stringValue.get());
+
+ Optional<String> nullStringValue = JSONB.fromJson("null", Optional.class);
+ assertTrue(!nullStringValue.isPresent());
+
+ //OptionalInt
+ OptionalInt optionalInt = JSONB.fromJson("1", OptionalInt.class);
+ assertTrue(optionalInt.isPresent());
+ assertTrue(optionalInt.getAsInt() == 1);
+
+ OptionalInt emptyOptionalInt = JSONB.fromJson("null", OptionalInt.class);
+ assertTrue(!emptyOptionalInt.isPresent());
+
+ //OptionalLong
+ OptionalLong optionalLong = JSONB.fromJson("123", OptionalLong.class);
+ assertTrue(optionalLong.isPresent());
+ assertTrue(optionalLong.getAsLong() == 123L);
+
+ OptionalLong emptyOptionalLong = JSONB.fromJson("null", OptionalLong.class);
+ assertTrue(!emptyOptionalLong.isPresent());
+
+ //OptionalDouble
+ OptionalDouble optionalDouble = JSONB.fromJson("1.2", OptionalDouble.class);
+ assertTrue(optionalDouble.isPresent());
+ assertTrue(optionalDouble.getAsDouble() == 1.2);
+
+ OptionalDouble emptyOptionalDouble = JSONB.fromJson("null", OptionalDouble.class);
+ assertTrue(!emptyOptionalDouble.isPresent());
+ */
+
+ OptionalClass optionalClass = JSONB.fromJson("{\"optionalField\":\"value\"}", OptionalClass.class);
+ assertTrue(optionalClass.optionalField.isPresent());
+ assertEquals("value", optionalClass.optionalField.get());
+
+ OptionalClass emptyOptionalClass = JSONB.fromJson("{}", OptionalClass.class);
+ assertTrue(!emptyOptionalClass.optionalField.isPresent());
+
+ OptionalClass nullOptionalClass = JSONB.fromJson("{\"optionalField\":null}", OptionalClass.class);
+ assertFalse(nullOptionalClass.optionalField.isPresent());
+ }
+
+ public static void toJsonAccessors() {
+
+ AccessorsClass accessorsClass = new AccessorsClass();
+ accessorsClass.setPrivateFieldWithPrivateAccessors(1);
+ accessorsClass.setPrivateFieldWithPublicAccessors(2);
+ accessorsClass.publicField = 3;
+
+ assertEquals("{\"privateFieldWithPublicAccessors\":2,\"publicField\":3,\"valueWithoutField\":1}", JSONB.toJson(accessorsClass));
+ }
+
+ public static void fromJsonAccessors() {
+ AccessorsClass accessorsClass = JSONB.fromJson(
+ "{\"privateFieldWithPrivateAccessors\":5,\"valueWithoutField\":7," +
+ "\"unknownValue\":11,\"transientValue\":9,\"privateFieldWithPublicAccessors\":4,\"publicField\":9," +
+ "\"protectedField\":8,\"defaultField\":13,\"privateField\":17}",
+ AccessorsClass.class);
+
+ assertEquals(7, accessorsClass.transientValue.intValue());
+ assertEquals(4, accessorsClass.privateFieldWithPublicAccessors.intValue());
+ assertEquals(9, accessorsClass.publicField.intValue());
+ assertNull(accessorsClass.privateFieldWithPrivateAccessors);
+ assertNull(accessorsClass.privateField);
+ assertNull(accessorsClass.defaultField);
+ assertNull(accessorsClass.protectedField);
+ }
+
+ static class AccessorsClass {
+ private Integer privateField;
+
+ protected Integer protectedField;
+
+ Integer defaultField;
+
+ public Integer publicField;
+
+ public transient Integer transientValue;
+
+ private Integer privateFieldWithPrivateAccessors;
+
+ private Integer privateFieldWithPublicAccessors;
+
+ private Integer getPrivateFieldWithPrivateAccessors() {
+ return privateFieldWithPrivateAccessors;
+ }
+
+ private void setPrivateFieldWithPrivateAccessors(Integer privateFieldWithPrivateAccessors) {
+ this.privateFieldWithPrivateAccessors = privateFieldWithPrivateAccessors;
+ }
+
+ public Integer getPrivateFieldWithPublicAccessors() {
+ return privateFieldWithPublicAccessors;
+ }
+
+ public void setPrivateFieldWithPublicAccessors(Integer privateFieldWithPublicAccessors) {
+ this.privateFieldWithPublicAccessors = privateFieldWithPublicAccessors;
+ }
+
+ public Integer getValueWithoutField() {
+ return 1;
+ }
+
+ public void setValueWithoutField(Integer valueWithoutField) {
+ transientValue = valueWithoutField;
+ }
+ }
+
+ static class OptionalClass {
+ public Optional<String> optionalField = Optional.empty();
+
+ public OptionalClass() {
+ }
+ }
+
+ static class ModifiersClass {
+ public final String finalField = "finalValue";
+ public static String staticField = "staticValue";
+ public transient String transientField = "transientValue";
+ public String regularField = "regularValue";
+
+ public ModifiersClass() {
+ }
+ }
+
+ static class POJOWithInitialValue {
+ public Integer id = 4;
+ public String name;
+
+ public POJOWithInitialValue() {
+ }
+ }
+
+ static class AttributesOrderingClass {
+ public String aField;
+ public String cField;
+ public String bField;
+
+ public AttributesOrderingClass() {
+ }
+ }
+
+ static class AttributesOrderingWithInheritance extends AttributesOrderingClass {
+ public String aa;
+ public String cc;
+ public String bb;
+
+ public AttributesOrderingWithInheritance() {
+ }
+ }
+
+ static class AttributesOrderingWithCounterClass {
+ private transient int counter = 0;
+
+ private String first = "first";
+
+ private String second = "second";
+
+ private String third = "third";
+
+ public AttributesOrderingWithCounterClass() {
+ }
+
+ public String getFirst() {
+ return first;
+ }
+
+ public void setFirst(String first) {
+ this.first = first + (counter++);
+ }
+
+ public String getSecond() {
+ return second;
+ }
+
+ public void setSecond(String second) {
+ this.second = second + (counter++);
+ }
+
+ public String getThird() {
+ return third;
+ }
+
+ public void setThird(String third) {
+ this.third = third + (counter++);
+ }
+ }
+//CHECKSTYLE:ON
+}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
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 0bc1a26..ac3e6b3 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
@@ -18,6 +18,7 @@
*/
package org.apache.johnzon.mapper;
+import org.apache.johnzon.core.JsonLongImpl;
import org.apache.johnzon.mapper.access.AccessMode;
import org.apache.johnzon.mapper.converter.EnumConverter;
import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
@@ -36,6 +37,7 @@ import javax.json.JsonValue.ValueType;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory;
import javax.xml.bind.DatatypeConverter;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -292,6 +294,28 @@ public class Mapper {
}
public void writeObject(final Object object, final Writer stream) {
+ if (JsonValue.class.isInstance(object)) {
+ try {
+ stream.write(object.toString());
+ } catch (final IOException e) {
+ throw new MapperException(e);
+ } finally {
+ if (close) {
+ try {
+ stream.close();
+ } catch (final IOException e) {
+ // no-op
+ }
+ } else {
+ try {
+ stream.flush();
+ } catch (final IOException e) {
+ // no-op
+ }
+ }
+ }
+ return;
+ }
final JsonGenerator generator = generatorFactory.createGenerator(stream);
doWriteHandlingNullObject(object, generator);
}
@@ -324,6 +348,15 @@ public class Mapper {
generator.writeStartObject().writeEnd().close();
return;
}
+ if (JsonObject.class.isInstance(object)) {
+ final JsonObject jsonObject = JsonObject.class.cast(object);
+ generator.writeStartObject();
+ for (final Map.Entry<String, JsonValue> value : jsonObject.entrySet()) {
+ generator.write(value.getKey(), value.getValue());
+ }
+ generator.writeEnd().close();
+ return;
+ }
//JsonGenerator gen = null;
try {
@@ -499,6 +532,8 @@ public class Mapper {
}
newGen = newGen.writeEnd();
}
+ } else if (o == null) {
+ newGen = generator.writeNull();
} else {
newGen = doWriteObject(generator, o);
}
@@ -520,7 +555,11 @@ public class Mapper {
private <T> T mapObject(final Type clazz, final JsonReader reader) {
try {
- return (T) buildObject(clazz, reader.readObject());
+ final JsonObject object = reader.readObject();
+ if (JsonStructure.class == clazz || JsonObject.class == clazz) {
+ return (T) object;
+ }
+ return (T) buildObject(clazz, object);
} catch (final Exception e) {
throw new MapperException(e);
} finally {
@@ -574,17 +613,27 @@ public class Mapper {
public <T> T[] readArray(final Reader stream, final Class<T> clazz) {
final JsonReader reader = readerFactory.createReader(stream);
- return mapArray(clazz, reader);
+ return (T[]) mapArray(clazz, reader);
+ }
+
+ public <T> T readTypedArray(final InputStream stream, final Class<?> elementType, final Class<T> arrayType) {
+ final JsonReader reader = readerFactory.createReader(stream);
+ return arrayType.cast(mapArray(elementType, reader));
+ }
+
+ public <T> T readTypedArray(final Reader stream, final Class<?> elementType, final Class<T> arrayType) {
+ final JsonReader reader = readerFactory.createReader(stream);
+ return arrayType.cast(mapArray(elementType, reader));
}
public <T> T[] readArray(final InputStream stream, final Class<T> clazz) {
final JsonReader reader = readerFactory.createReader(stream);
- return mapArray(clazz, reader);
+ return (T[]) mapArray(clazz, reader);
}
- private <T> T[] mapArray(final Class<T> clazz, final JsonReader reader) {
+ private Object mapArray(final Class<?> clazz, final JsonReader reader) {
try {
- return (T[]) buildArrayWithComponentType(reader.readArray(), clazz, null);
+ return buildArrayWithComponentType(reader.readArray(), clazz, null);
} catch (final Exception e) {
throw new MapperException(e);
} finally {
@@ -633,18 +682,46 @@ public class Mapper {
keyType = fieldArgTypes[0];
}
+ final boolean any = fieldArgTypes.length < 2 || fieldArgTypes[1] == Object.class;
for (final Map.Entry<String, JsonValue> value : object.entrySet()) {
- map.put(convertTo(keyType, value.getKey()), toObject(value.getValue(), fieldArgTypes[1], null));
+ final JsonValue jsonValue = value.getValue();
+ if (JsonLongImpl.class.isInstance(jsonValue) && any) {
+ final JsonNumber number = JsonNumber.class.cast(jsonValue);
+ final int integer = number.intValue();
+ final long asLong = number.longValue();
+ if (integer == asLong) {
+ map.put(value.getKey(), integer);
+ } else {
+ map.put(value.getKey(), asLong);
+ }
+ } else if (JsonNumber.class.isInstance(jsonValue) && any) {
+ final JsonNumber number = JsonNumber.class.cast(jsonValue);
+ map.put(value.getKey(), !number.isIntegral() ? number.bigDecimalValue() : number.intValue());
+ } else if (JsonString.class.isInstance(jsonValue) && any) {
+ map.put(value.getKey(), JsonString.class.cast(jsonValue).getString());
+ } else {
+ map.put(convertTo(keyType, value.getKey()), toObject(jsonValue, fieldArgTypes[1], null));
+ }
}
return map;
}
}
+ } 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()) {
+ map.put(value.getKey(), toObject(value.getValue(), Object.class, null));
+ }
+ return map;
}
}
if (classMapping == null) {
throw new MapperException("Can't map " + type);
}
+ if (classMapping.factory == null) {
+ throw new IllegalArgumentException(classMapping.clazz + " not instantiable");
+ }
+
final Object t = classMapping.factory.getParameterTypes().length == 0 ?
classMapping.factory.create(null) : classMapping.factory.create(createParameters(classMapping, object));
for (final Map.Entry<String, Mappings.Setter> setter : classMapping.setters.entrySet()) {
@@ -659,9 +736,13 @@ public class Mapper {
}
final AccessMode.Writer setterMethod = value.writer;
- final Object convertedValue = toValue(jsonValue, value.converter, value.itemConverter, value.paramType);
- if (convertedValue != null) {
- setterMethod.write(t, convertedValue);
+ if (jsonValue == JsonValue.NULL) { // forced
+ setterMethod.write(t, null);
+ } else {
+ final Object convertedValue = toValue(jsonValue, value.converter, value.itemConverter, value.paramType);
+ if (convertedValue != null) {
+ setterMethod.write(t, convertedValue);
+ }
}
}
@@ -687,7 +768,7 @@ public class Mapper {
}
private Object toObject(final JsonValue jsonValue, final Type type, final Converter<?> itemConverter) throws Exception {
- if (jsonValue == null || jsonValue == JsonValue.NULL) {
+ if (jsonValue == null || JsonValue.NULL == jsonValue) {
return null;
}
@@ -773,7 +854,7 @@ public class Mapper {
if (type == BigDecimal.class) {
return number.bigDecimalValue();
}
- } else if (JsonString.class.isInstance(jsonValue) || Object.class == type) {
+ } else if (JsonString.class.isInstance(jsonValue)) {
if (JsonString.class == type) {
return jsonValue;
}
@@ -820,7 +901,7 @@ public class Mapper {
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) {
+ } 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());
@@ -835,7 +916,7 @@ public class Mapper {
}
for (final JsonValue value : jsonArray) {
- collection.add(toObject(value, mapping.arg, itemConverter));
+ collection.add(value == JsonValue.NULL ? null : toObject(value, mapping.arg, itemConverter));
}
if (EnumSet.class == mapping.raw) {
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
index 8a3a359..b89a01d 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/AccessMode.java
@@ -22,6 +22,7 @@ import org.apache.johnzon.mapper.Converter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
+import java.util.Comparator;
import java.util.Map;
public interface AccessMode {
@@ -50,6 +51,7 @@ public interface AccessMode {
}
Factory findFactory(Class<?> clazz);
+ Comparator<String> fieldComparator(Class<?> clazz);
Map<String, Reader> findReaders(Class<?> clazz);
Map<String, Writer> findWriters(Class<?> clazz);
}
http://git-wip-us.apache.org/repos/asf/incubator-johnzon/blob/a409da7d/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
index 94642bf..70ab5af 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/BaseAccessMode.java
@@ -32,6 +32,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -58,6 +59,11 @@ public abstract class BaseAccessMode implements AccessMode {
protected abstract Map<String,Writer> doFindWriters(Class<?> clazz);
@Override
+ public Comparator<String> fieldComparator(final Class<?> clazz) {
+ return null;
+ }
+
+ @Override
public Map<String, Reader> findReaders(final Class<?> clazz) {
return sanitize(clazz, doFindReaders(clazz));
}
@@ -240,8 +246,9 @@ public abstract class BaseAccessMode implements AccessMode {
return clazz;
}
- if (clazz.getSuperclass() != Object.class) {
- return findClass(clazz.getSuperclass(), genericDeclaration);
+ final Class<?> superclass = clazz.getSuperclass();
+ if (superclass != null && superclass != Object.class) {
+ return findClass(superclass, genericDeclaration);
}
return null;