diff --git a/ChangeLog.md b/ChangeLog.md index ac54b93..62c5057 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +- added useTypeHint option to VPack.Builder + ## [2.3.1] - 2020-05-05 - shaded jackson dependency diff --git a/pom.xml b/pom.xml index 538a878..bae10de 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.arangodb velocypack - 2.3.1 + 2.4.0-SNAPSHOT 2017 jar diff --git a/src/main/java/com/arangodb/velocypack/VPack.java b/src/main/java/com/arangodb/velocypack/VPack.java index 0e46fac..929b52d 100644 --- a/src/main/java/com/arangodb/velocypack/VPack.java +++ b/src/main/java/com/arangodb/velocypack/VPack.java @@ -49,6 +49,7 @@ public class VPack { private static final String ATTR_KEY = "key"; private static final String ATTR_VALUE = "value"; private static final String DEFAULT_TYPE_KEY = "_class"; + private static final boolean DEFAULT_USE_TYPE_HINTS = true; private final Map> serializers; private final Map> enclosingSerializers; @@ -65,6 +66,7 @@ public class VPack { private final VPackDeserializationContext deserializationContext; private final boolean serializeNullValues; private final String typeKey; + private final boolean useTypeHints; private final VPackBuilderUtils builderUtils; private final VPackCreatorMethodUtils vPackCreatorMethodUtils; @@ -83,6 +85,7 @@ public static class Builder implements VPackSetupContext { private final Map, VPackAnnotationFieldNaming> annotationFieldNaming; private final Map> keyMapAdapters; private String typeKey; + private Boolean useTypeHints; public Builder() { super(); @@ -99,6 +102,7 @@ public Builder() { annotationFieldNaming = new HashMap<>(); keyMapAdapters = new HashMap<>(); typeKey = null; + useTypeHints = null; instanceCreators.put(Iterable.class, VPackInstanceCreators.ITERABLE); instanceCreators.put(Collection.class, VPackInstanceCreators.COLLECTION); @@ -319,11 +323,24 @@ public Builder registerKeyMapAdapter(final Type type, final VPackKeyMapAdapter(serializers), new HashMap<>(enclosingSerializers), new HashMap<>(deserializers), new HashMap<>(deserializersWithSelfNullHandle), @@ -331,7 +348,10 @@ public synchronized VPack build() { fieldNamingStrategy, new HashMap<>(deserializersByName), new HashMap<>(deserializersByNameWithSelfNullHandle), new HashMap<>(annotationFieldFilter), new HashMap<>(annotationFieldNaming), - keyMapAdapters, typeKey != null ? typeKey : DEFAULT_TYPE_KEY); + keyMapAdapters, + typeKey != null ? typeKey : DEFAULT_TYPE_KEY, + useTypeHints != null ? useTypeHints : DEFAULT_USE_TYPE_HINTS + ); } } @@ -345,7 +365,7 @@ private VPack(final Map> serializers, final Map>> deserializersByNameWithSelfNullHandle, final Map, VPackAnnotationFieldFilter> annotationFieldFilter, final Map, VPackAnnotationFieldNaming> annotationFieldNaming, - final Map> keyMapAdapters, final String typeKey) { + final Map> keyMapAdapters, final String typeKey, final boolean useTypeHints) { super(); this.serializers = serializers; this.enclosingSerializers = enclosingSerializers; @@ -358,6 +378,7 @@ private VPack(final Map> serializers, this.deserializersByNameWithSelfNullHandle = deserializersByNameWithSelfNullHandle; this.keyMapAdapters = keyMapAdapters; this.typeKey = typeKey; + this.useTypeHints = useTypeHints; builderUtils = new VPackBuilderUtils(); vPackCreatorMethodUtils = new VPackCreatorMethodUtils(); @@ -480,7 +501,7 @@ private T deserializeObject( } private Type determineType(final VPackSlice vpack, final Type type) { - if (!vpack.isObject()) { + if (!useTypeHints || !vpack.isObject()) { return type; } final VPackSlice clazz = vpack.get(typeKey); @@ -933,6 +954,12 @@ private void addValue( } else if (shouldAddTypeHint(type, value, fieldInfo)) { addValue(name, value.getClass(), value, builder, fieldInfo, Collections.singletonMap(typeKey, value.getClass().getName())); + } else if (value instanceof Iterable) { + serializeIterable(name, value, builder, null); + } else if (value instanceof Map) { + serializeMap(name, value, builder, String.class, additionalFields); + } else if (value.getClass().isArray()) { + serializeArray(name, value, builder, null); } else { serializeObject(name, value, builder, additionalFields); } @@ -944,6 +971,9 @@ private boolean shouldAddTypeHint( final Object value, final FieldInfo fieldInfo ) { + if (!useTypeHints) { + return false; + } final AnnotatedElement referencingElement = fieldInfo != null ? fieldInfo.getReferencingElement() : null; final BuilderInfo builderInfo = builderUtils.getBuilderInfo(type, referencingElement); if (builderInfo != null) { diff --git a/src/main/java/com/arangodb/velocypack/VPackSetupContext.java b/src/main/java/com/arangodb/velocypack/VPackSetupContext.java index 1839655..7598b51 100644 --- a/src/main/java/com/arangodb/velocypack/VPackSetupContext.java +++ b/src/main/java/com/arangodb/velocypack/VPackSetupContext.java @@ -60,8 +60,8 @@ C annotationFieldFilter( final VPackAnnotationFieldFilter fieldFilter); C annotationFieldNaming( - final Class type, - final VPackAnnotationFieldNaming fieldNaming); + final Class type, + final VPackAnnotationFieldNaming fieldNaming); C registerKeyMapAdapter(final Type type, final VPackKeyMapAdapter adapter); @@ -69,4 +69,8 @@ C annotationFieldNaming( C registerModules(VPackModule... modules); + C typeKey(final String typeKey); + + C useTypeHints(final boolean useTypeHints); + } diff --git a/src/test/java/com/arangodb/velocypack/VPackWithoutTypeHintTest.java b/src/test/java/com/arangodb/velocypack/VPackWithoutTypeHintTest.java new file mode 100644 index 0000000..aa3846a --- /dev/null +++ b/src/test/java/com/arangodb/velocypack/VPackWithoutTypeHintTest.java @@ -0,0 +1,320 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed 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. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.velocypack; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.*; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +/** + * @author Michele Rastelli + */ +@RunWith(Parameterized.class) +public class VPackWithoutTypeHintTest { + + private final boolean useTypeHints; + + @Parameterized.Parameters + public static Collection useTypeHint() { + return Arrays.asList(true, false); + } + + public VPackWithoutTypeHintTest(final boolean useTypeHints) { + this.useTypeHints = useTypeHints; + } + + public static class NestedArray { + public String[] value; + } + + @Test + public void nestedArrayWithoutTypeInformation() { + NestedArray input = new NestedArray(); + input.value = new String[]{"a", "b", "c"}; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedArray output = vpack.deserialize(slice, NestedArray.class); + assertThat(output.value, instanceOf(String[].class)); + + assertThat(output.value, equalTo(input.value)); + } + + public static class NestedIterable { + public Iterable value; + } + + @Test + public void nestedIterableWithoutTypeInformation() { + NestedIterable input = new NestedIterable(); + input.value = Arrays.asList("a", "b", "c"); + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedIterable output = vpack.deserialize(slice, NestedIterable.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(input.value)); + } + + public static class NestedCollection { + public Collection value; + } + + @Test + public void nestedCollectionWithoutTypeInformation() { + NestedCollection input = new NestedCollection(); + input.value = Arrays.asList("a", "b", "c"); + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedCollection output = vpack.deserialize(slice, NestedCollection.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(input.value)); + } + + public static class NestedMap { + public Map value; + } + + @Test + public void nestedMapWithoutTypeInformation() { + Map map = new HashMap<>(); + map.put("a", "b"); + + NestedMap input = new NestedMap(); + input.value = map; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isObject(), is(true)); + if (!useTypeHints) { + assertThat(slice.get("_class").isNone(), is(true)); + assertThat(slice.get("value").get("_class").isNone(), is(true)); + } + NestedMap output = vpack.deserialize(slice, NestedMap.class); + assertThat(output.value, instanceOf(Map.class)); + + assertThat(output.value, equalTo(input.value)); + } + + public static class NestedObject { + public Object value; + } + + @Test + public void nestedObjectListWithoutTypeInformation() { + NestedObject input = new NestedObject(); + input.value = Arrays.asList("a", "b", "c"); + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedObject output = vpack.deserialize(slice, NestedObject.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(input.value)); + } + + @Test + public void nestedObjectArrayWithoutTypeInformation() { + NestedObject input = new NestedObject(); + input.value = new String[]{"a", "b", "c"}; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedObject output = vpack.deserialize(slice, NestedObject.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(((List) output.value).toArray(), equalTo(input.value)); + } + + @Test + public void nestedObjectMapWithoutTypeInformation() { + Map map = new HashMap<>(); + map.put("a", "b"); + + NestedObject input = new NestedObject(); + input.value = map; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isObject(), is(true)); + if (!useTypeHints) { + assertThat(slice.get("_class").isNone(), is(true)); + assertThat(slice.get("value").get("_class").isNone(), is(true)); + } + + NestedObject output = vpack.deserialize(slice, NestedObject.class); + assertThat(output.value, instanceOf(Map.class)); + + ((Map) output.value).remove("_class"); + assertThat(output.value, equalTo(input.value)); + } + + public static class NestedGeneric { + public T value; + } + + @Test + public void nestedGenericOfArrayWithoutTypeInformation() { + NestedGeneric input = new NestedGeneric<>(); + input.value = new String[]{"a", "b", "c"}; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedGeneric output = vpack.deserialize(slice, NestedGeneric.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(Arrays.asList(input.value))); + } + + @Test + public void nestedGenericOfIterableWithoutTypeInformation() { + NestedGeneric> input = new NestedGeneric<>(); + input.value = Arrays.asList("a", "b", "c"); + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedGeneric output = vpack.deserialize(slice, NestedGeneric.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(input.value)); + } + + @Test + public void nestedGenericOfCollectionWithoutTypeInformation() { + NestedGeneric> input = new NestedGeneric<>(); + input.value = Arrays.asList("a", "b", "c"); + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isArray(), is(true)); + if (!useTypeHints) + assertThat(slice.get("_class").isNone(), is(true)); + + NestedGeneric output = vpack.deserialize(slice, NestedGeneric.class); + assertThat(output.value, instanceOf(List.class)); + + assertThat(output.value, equalTo(input.value)); + } + + @Test + public void nestedGenericOfMapWithoutTypeInformation() { + Map map = new HashMap<>(); + map.put("a", "b"); + + NestedGeneric> input = new NestedGeneric<>(); + input.value = map; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isObject(), is(true)); + if (!useTypeHints) { + assertThat(slice.get("_class").isNone(), is(true)); + assertThat(slice.get("value").get("_class").isNone(), is(true)); + } + + NestedGeneric output = vpack.deserialize(slice, NestedGeneric.class); + assertThat(output.value, instanceOf(Map.class)); + + ((Map) output.value).remove("_class"); + assertThat(output.value, equalTo(input.value)); + } + + @Test + public void nestedGenericOfObjectWithoutTypeInformation() { + Map map = new HashMap<>(); + map.put("a", "b"); + + NestedGeneric input = new NestedGeneric<>(); + input.value = map; + + final VPack vpack = new VPack.Builder().useTypeHints(useTypeHints).build(); + final VPackSlice slice = vpack.serialize(input); + + assertThat(slice.isObject(), is(true)); + assertThat(slice.get("value").isObject(), is(true)); + if (!useTypeHints) { + assertThat(slice.get("_class").isNone(), is(true)); + assertThat(slice.get("value").get("_class").isNone(), is(true)); + } + + NestedGeneric output = vpack.deserialize(slice, NestedGeneric.class); + assertThat(output.value, instanceOf(Map.class)); + + ((Map) output.value).remove("_class"); + assertThat(output.value, equalTo(input.value)); + } + +}