Skip to content

Commit 9534c88

Browse files
author
Mark
committed
added configuration for custom annotations within VPack
de-/serialization
1 parent 28b11a4 commit 9534c88

File tree

6 files changed

+260
-12
lines changed

6 files changed

+260
-12
lines changed

ChangeLog

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ v4.1.5 (2017-01-xx)
22
---------------------------
33
* fixed VPack parsing of fields of type Object
44
* fixed VPack serializing of array with null values (issue #88)
5+
* added configuration for custom annotations within VPack de-/serialization
56

67
v4.1.4 (2016-12-19)
78
---------------------------

src/main/java/com/arangodb/velocypack/VPack.java

+45-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.arangodb.velocypack;
2222

23+
import java.lang.annotation.Annotation;
2324
import java.lang.reflect.Array;
2425
import java.lang.reflect.InvocationTargetException;
2526
import java.lang.reflect.ParameterizedType;
@@ -37,6 +38,8 @@
3738
import java.util.UUID;
3839

3940
import com.arangodb.velocypack.VPackBuilder.BuilderOptions;
41+
import com.arangodb.velocypack.annotations.Expose;
42+
import com.arangodb.velocypack.annotations.SerializedName;
4043
import com.arangodb.velocypack.exception.VPackException;
4144
import com.arangodb.velocypack.exception.VPackParserException;
4245
import com.arangodb.velocypack.internal.DefaultVPackBuilderOptions;
@@ -77,6 +80,8 @@ public static class Builder {
7780
private final BuilderOptions builderOptions;
7881
private boolean serializeNullValues;
7982
private VPackFieldNamingStrategy fieldNamingStrategy;
83+
private final Map<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>> annotationFieldFilter;
84+
private final Map<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>> annotationFieldNaming;
8085

8186
public Builder() {
8287
super();
@@ -86,6 +91,8 @@ public Builder() {
8691
instanceCreators = new HashMap<Type, VPackInstanceCreator<?>>();
8792
builderOptions = new DefaultVPackBuilderOptions();
8893
serializeNullValues = false;
94+
annotationFieldFilter = new HashMap<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>>();
95+
annotationFieldNaming = new HashMap<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>>();
8996

9097
instanceCreators.put(Collection.class, VPackInstanceCreators.COLLECTION);
9198
instanceCreators.put(List.class, VPackInstanceCreators.LIST);
@@ -139,6 +146,24 @@ public Builder() {
139146
deserializers.put(java.sql.Timestamp.class, VPackDeserializers.SQL_TIMESTAMP);
140147
deserializers.put(VPackSlice.class, VPackDeserializers.VPACK);
141148
deserializers.put(UUID.class, VPackDeserializers.UUID);
149+
150+
annotationFieldFilter.put(Expose.class, new VPackAnnotationFieldFilter<Expose>() {
151+
@Override
152+
public boolean serialize(final Expose annotation) {
153+
return annotation.serialize();
154+
}
155+
156+
@Override
157+
public boolean deserialize(final Expose annotation) {
158+
return annotation.deserialize();
159+
}
160+
});
161+
annotationFieldNaming.put(SerializedName.class, new VPackAnnotationFieldNaming<SerializedName>() {
162+
@Override
163+
public String name(final SerializedName annotation) {
164+
return annotation.value();
165+
}
166+
});
142167
}
143168

144169
public <T> VPack.Builder registerSerializer(final Type type, final VPackSerializer<T> serializer) {
@@ -189,17 +214,33 @@ public VPack.Builder fieldNamingStrategy(final VPackFieldNamingStrategy fieldNam
189214
return this;
190215
}
191216

217+
public <A extends Annotation> VPack.Builder annotationFieldFilter(
218+
final Class<A> type,
219+
final VPackAnnotationFieldFilter<A> fieldFilter) {
220+
annotationFieldFilter.put(type, fieldFilter);
221+
return this;
222+
}
223+
224+
public <A extends Annotation> VPack.Builder annotationFieldNaming(
225+
final Class<A> type,
226+
final VPackAnnotationFieldNaming<A> fieldNaming) {
227+
annotationFieldNaming.put(type, fieldNaming);
228+
return this;
229+
}
230+
192231
public VPack build() {
193232
return new VPack(serializers, deserializers, instanceCreators, builderOptions, serializeNullValues,
194-
fieldNamingStrategy, deserializersByName);
233+
fieldNamingStrategy, deserializersByName, annotationFieldFilter, annotationFieldNaming);
195234
}
196235

197236
}
198237

199238
private VPack(final Map<Type, VPackSerializer<?>> serializers, final Map<Type, VPackDeserializer<?>> deserializers,
200239
final Map<Type, VPackInstanceCreator<?>> instanceCreators, final BuilderOptions builderOptions,
201240
final boolean serializeNullValues, final VPackFieldNamingStrategy fieldNamingStrategy,
202-
final Map<String, Map<Type, VPackDeserializer<?>>> deserializersByName) {
241+
final Map<String, Map<Type, VPackDeserializer<?>>> deserializersByName,
242+
final Map<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>> annotationFieldFilter,
243+
final Map<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>> annotationFieldNaming) {
203244
super();
204245
this.serializers = serializers;
205246
this.deserializers = deserializers;
@@ -208,7 +249,8 @@ private VPack(final Map<Type, VPackSerializer<?>> serializers, final Map<Type, V
208249
this.serializeNullValues = serializeNullValues;
209250
this.deserializersByName = deserializersByName;
210251
keyMapAdapters = new HashMap<Type, VPackKeyMapAdapter<?>>();
211-
cache = new VPackCache(fieldNamingStrategy);
252+
253+
cache = new VPackCache(fieldNamingStrategy, annotationFieldFilter, annotationFieldNaming);
212254
serializationContext = new VPackSerializationContext() {
213255
@Override
214256
public void serialize(final VPackBuilder builder, final String attribute, final Object entity)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* DISCLAIMER
3+
*
4+
* Copyright 2016 ArangoDB GmbH, Cologne, Germany
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
*/
20+
21+
package com.arangodb.velocypack;
22+
23+
import java.lang.annotation.Annotation;
24+
25+
/**
26+
* @author Mark - mark at arangodb.com
27+
*
28+
*/
29+
public interface VPackAnnotationFieldFilter<A extends Annotation> {
30+
31+
boolean serialize(A annotation);
32+
33+
boolean deserialize(A annotation);
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* DISCLAIMER
3+
*
4+
* Copyright 2016 ArangoDB GmbH, Cologne, Germany
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
*/
20+
21+
package com.arangodb.velocypack;
22+
23+
import java.lang.annotation.Annotation;
24+
25+
/**
26+
* @author Mark - mark at arangodb.com
27+
*
28+
*/
29+
public interface VPackAnnotationFieldNaming<A extends Annotation> {
30+
31+
String name(A annotation);
32+
33+
}

src/main/java/com/arangodb/velocypack/internal/VPackCache.java

+49-9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.arangodb.velocypack.internal;
2222

23+
import java.lang.annotation.Annotation;
2324
import java.lang.reflect.Field;
2425
import java.lang.reflect.Modifier;
2526
import java.lang.reflect.ParameterizedType;
@@ -36,9 +37,12 @@
3637
import java.util.Set;
3738
import java.util.concurrent.ConcurrentHashMap;
3839

40+
import org.slf4j.Logger;
41+
import org.slf4j.LoggerFactory;
42+
43+
import com.arangodb.velocypack.VPackAnnotationFieldFilter;
44+
import com.arangodb.velocypack.VPackAnnotationFieldNaming;
3945
import com.arangodb.velocypack.VPackFieldNamingStrategy;
40-
import com.arangodb.velocypack.annotations.Expose;
41-
import com.arangodb.velocypack.annotations.SerializedName;
4246

4347
/**
4448
* @author Mark - mark at arangodb.com
@@ -81,11 +85,18 @@ public boolean isDeserialize() {
8185
public abstract Object get(Object obj) throws IllegalAccessException;
8286
}
8387

88+
private static final Logger LOGGER = LoggerFactory.getLogger(VPackCache.class);
89+
8490
private final Map<Type, Map<String, FieldInfo>> cache;
8591
private final Comparator<Entry<String, FieldInfo>> fieldComparator;
8692
private final VPackFieldNamingStrategy fieldNamingStrategy;
8793

88-
public VPackCache(final VPackFieldNamingStrategy fieldNamingStrategy) {
94+
private final Map<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>> annotationFilter;
95+
private final Map<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>> annotationFieldNaming;
96+
97+
public VPackCache(final VPackFieldNamingStrategy fieldNamingStrategy,
98+
final Map<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>> annotationFieldFilter,
99+
final Map<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>> annotationFieldNaming) {
89100
super();
90101
cache = new ConcurrentHashMap<Type, Map<String, FieldInfo>>();
91102
fieldComparator = new Comparator<Map.Entry<String, FieldInfo>>() {
@@ -95,6 +106,8 @@ public int compare(final Entry<String, FieldInfo> o1, final Entry<String, FieldI
95106
}
96107
};
97108
this.fieldNamingStrategy = fieldNamingStrategy;
109+
this.annotationFilter = annotationFieldFilter;
110+
this.annotationFieldNaming = annotationFieldNaming;
98111
}
99112

100113
public Map<String, FieldInfo> getFields(final Type entityClass) {
@@ -131,18 +144,45 @@ private Map<String, FieldInfo> sort(final Set<Entry<String, FieldInfo>> entrySet
131144
return sorted;
132145
}
133146

147+
@SuppressWarnings("unchecked")
134148
private FieldInfo createFieldInfo(final Field field) {
135149
String fieldName = field.getName();
136150
if (fieldNamingStrategy != null) {
137151
fieldName = fieldNamingStrategy.translateName(field);
138152
}
139-
final SerializedName annotationName = field.getAnnotation(SerializedName.class);
140-
if (annotationName != null) {
141-
fieldName = annotationName.value();
153+
boolean found = false;
154+
for (final Entry<Class<? extends Annotation>, VPackAnnotationFieldNaming<? extends Annotation>> entry : annotationFieldNaming
155+
.entrySet()) {
156+
final Annotation annotation = field.getAnnotation(entry.getKey());
157+
if (annotation != null) {
158+
fieldName = ((VPackAnnotationFieldNaming<Annotation>) entry.getValue()).name(annotation);
159+
if (found) {
160+
LOGGER.warn(String.format(
161+
"Found additional annotation %s for field %s. Override previous annotation informations.",
162+
entry.getKey().getSimpleName(), field.getName()));
163+
}
164+
found = true;
165+
}
166+
}
167+
boolean serialize = true;
168+
boolean deserialize = true;
169+
found = false;
170+
for (final Entry<Class<? extends Annotation>, VPackAnnotationFieldFilter<? extends Annotation>> entry : annotationFilter
171+
.entrySet()) {
172+
final Annotation annotation = field.getAnnotation(entry.getKey());
173+
if (annotation != null) {
174+
final VPackAnnotationFieldFilter<Annotation> filter = (VPackAnnotationFieldFilter<Annotation>) entry
175+
.getValue();
176+
serialize = filter.serialize(annotation);
177+
deserialize = filter.deserialize(annotation);
178+
if (found) {
179+
LOGGER.warn(String.format(
180+
"Found additional annotation %s for field %s. Override previous annotation informations.",
181+
entry.getKey().getSimpleName(), field.getName()));
182+
}
183+
found = true;
184+
}
142185
}
143-
final Expose expose = field.getAnnotation(Expose.class);
144-
final boolean serialize = expose != null ? expose.serialize() : true;
145-
final boolean deserialize = expose != null ? expose.deserialize() : true;
146186
final Class<?> clazz = field.getType();
147187
final Type type;
148188
if (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {

src/test/java/com/arangodb/velocypack/VPackSerializeDeserializeTest.java

+97
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
import static org.hamcrest.Matchers.nullValue;
2626
import static org.junit.Assert.assertThat;
2727

28+
import java.lang.annotation.ElementType;
29+
import java.lang.annotation.Retention;
30+
import java.lang.annotation.RetentionPolicy;
31+
import java.lang.annotation.Target;
2832
import java.lang.reflect.Field;
2933
import java.math.BigDecimal;
3034
import java.math.BigInteger;
@@ -2804,6 +2808,99 @@ public void toExpose() throws VPackException {
28042808
assertThat(entity.f, is(nullValue()));
28052809
}
28062810

2811+
@Retention(RetentionPolicy.RUNTIME)
2812+
@Target(ElementType.FIELD)
2813+
private static @interface CustomFilterAnnotation {
2814+
boolean serialize() default true;
2815+
2816+
boolean deserialize() default true;
2817+
}
2818+
2819+
@Retention(RetentionPolicy.RUNTIME)
2820+
@Target(ElementType.FIELD)
2821+
private static @interface CustomNamingAnnotation {
2822+
String name();
2823+
}
2824+
2825+
private static class CustomAnEntity {
2826+
@CustomFilterAnnotation(serialize = false)
2827+
private String a = null;
2828+
@CustomFilterAnnotation(deserialize = false)
2829+
private String b = null;
2830+
@CustomNamingAnnotation(name = "d")
2831+
@CustomFilterAnnotation(deserialize = false)
2832+
private String c = null;
2833+
2834+
public CustomAnEntity() {
2835+
super();
2836+
}
2837+
}
2838+
2839+
@Test
2840+
public void fromCutsomAnnotation() {
2841+
final CustomAnEntity entity = new CustomAnEntity();
2842+
entity.a = "1";
2843+
entity.b = "2";
2844+
entity.c = "3";
2845+
final VPackSlice vpack = new VPack.Builder().annotationFieldFilter(CustomFilterAnnotation.class,
2846+
new VPackAnnotationFieldFilter<CustomFilterAnnotation>() {
2847+
2848+
@Override
2849+
public boolean serialize(final CustomFilterAnnotation annotation) {
2850+
return annotation.serialize();
2851+
}
2852+
2853+
@Override
2854+
public boolean deserialize(final CustomFilterAnnotation annotation) {
2855+
return annotation.deserialize();
2856+
}
2857+
}).annotationFieldNaming(CustomNamingAnnotation.class,
2858+
new VPackAnnotationFieldNaming<CustomNamingAnnotation>() {
2859+
@Override
2860+
public String name(final CustomNamingAnnotation annotation) {
2861+
return annotation.name();
2862+
}
2863+
}).build().serialize(entity);
2864+
assertThat(vpack, is(notNullValue()));
2865+
assertThat(vpack.isObject(), is(true));
2866+
assertThat(vpack.get("a").isNone(), is(true));
2867+
assertThat(vpack.get("b").isString(), is(true));
2868+
assertThat(vpack.get("b").getAsString(), is("2"));
2869+
assertThat(vpack.get("c").isNone(), is(true));
2870+
assertThat(vpack.get("d").isString(), is(true));
2871+
assertThat(vpack.get("d").getAsString(), is("3"));
2872+
}
2873+
2874+
@Test
2875+
public void toCustomAnnotation() {
2876+
final VPackSlice vpack = new VPackBuilder().add(ValueType.OBJECT).add("a", "1").add("b", "2").add("c", "3")
2877+
.add("d", "4").close().slice();
2878+
2879+
final CustomAnEntity entity = new VPack.Builder().annotationFieldFilter(CustomFilterAnnotation.class,
2880+
new VPackAnnotationFieldFilter<CustomFilterAnnotation>() {
2881+
2882+
@Override
2883+
public boolean serialize(final CustomFilterAnnotation annotation) {
2884+
return annotation.serialize();
2885+
}
2886+
2887+
@Override
2888+
public boolean deserialize(final CustomFilterAnnotation annotation) {
2889+
return annotation.deserialize();
2890+
}
2891+
}).annotationFieldNaming(CustomNamingAnnotation.class,
2892+
new VPackAnnotationFieldNaming<CustomNamingAnnotation>() {
2893+
@Override
2894+
public String name(final CustomNamingAnnotation annotation) {
2895+
return annotation.name();
2896+
}
2897+
}).build().deserialize(vpack, CustomAnEntity.class);
2898+
assertThat(entity, is(notNullValue()));
2899+
assertThat(entity.a, is("1"));
2900+
assertThat(entity.b, is(nullValue()));
2901+
assertThat(entity.c, is(nullValue()));
2902+
}
2903+
28072904
@Test
28082905
public void directFromCollection() throws VPackException {
28092906
final Collection<String> list = new ArrayList<String>();

0 commit comments

Comments
 (0)