Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 05a639e

Browse files
authored
[DE-636] Non-interfering Jackson annotations (#513)
* removed JacksonAnnotationsInside from com.arangodb.serde.jackson.Key annotation * handle Jackson Serde annotations with custom JacksonAnnotationIntrospector
1 parent 7ad7d5f commit 05a639e

File tree

8 files changed

+298
-35
lines changed

8 files changed

+298
-35
lines changed
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package com.arangodb.serde;
2+
3+
import com.arangodb.serde.jackson.*;
4+
import com.arangodb.serde.jackson.json.JacksonJsonSerdeProvider;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
8+
import com.fasterxml.jackson.databind.node.ObjectNode;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.api.Test;
11+
12+
import java.io.IOException;
13+
import java.util.UUID;
14+
import java.util.function.BiFunction;
15+
import java.util.function.Function;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
class JacksonInterferenceTest {
20+
21+
private final ObjectMapper mapper = new ObjectMapper();
22+
private final ArangoSerde serde = new JacksonJsonSerdeProvider().create();
23+
24+
private FooField fooField;
25+
private FooProp fooProp;
26+
27+
static class FooField {
28+
@Id
29+
public String myId;
30+
@Key
31+
public String myKey;
32+
@Rev
33+
public String myRev;
34+
@From
35+
public String myFrom;
36+
@To
37+
public String myTo;
38+
}
39+
40+
static class FooProp {
41+
public String myId;
42+
public String myKey;
43+
public String myRev;
44+
public String myFrom;
45+
public String myTo;
46+
47+
@Id
48+
public String getMyId() {
49+
return myId;
50+
}
51+
52+
@Id
53+
public void setMyId(String myId) {
54+
this.myId = myId;
55+
}
56+
57+
@Key
58+
public String getMyKey() {
59+
return myKey;
60+
}
61+
62+
@Key
63+
public void setMyKey(String myKey) {
64+
this.myKey = myKey;
65+
}
66+
67+
@Rev
68+
public String getMyRev() {
69+
return myRev;
70+
}
71+
72+
@Rev
73+
public void setMyRev(String myRev) {
74+
this.myRev = myRev;
75+
}
76+
77+
@From
78+
public String getMyFrom() {
79+
return myFrom;
80+
}
81+
82+
@From
83+
public void setMyFrom(String myFrom) {
84+
this.myFrom = myFrom;
85+
}
86+
87+
@To
88+
public String getMyTo() {
89+
return myTo;
90+
}
91+
92+
@To
93+
public void setMyTo(String myTo) {
94+
this.myTo = myTo;
95+
}
96+
}
97+
98+
@BeforeEach
99+
void init() {
100+
fooField = new FooField();
101+
fooProp = new FooProp();
102+
103+
fooField.myId = "myId";
104+
fooProp.myId = "myId";
105+
106+
fooField.myKey = "myKey";
107+
fooProp.myKey = "myKey";
108+
109+
fooField.myRev = "myRev";
110+
fooProp.myRev = "myRev";
111+
112+
fooField.myFrom = "myFrom";
113+
fooProp.myFrom = "myFrom";
114+
115+
fooField.myTo = "myTo";
116+
fooProp.myTo = "myTo";
117+
}
118+
119+
@Test
120+
void serializeField() {
121+
// id
122+
testSerialize(fooField, "myId", fooField.myId, this::jacksonSerialize);
123+
testSerialize(fooField, "_id", fooField.myId, this::serdeSerialize);
124+
// key
125+
testSerialize(fooField, "myKey", fooField.myKey, this::jacksonSerialize);
126+
testSerialize(fooField, "_key", fooField.myKey, this::serdeSerialize);
127+
// rev
128+
testSerialize(fooField, "myRev", fooField.myRev, this::jacksonSerialize);
129+
testSerialize(fooField, "_rev", fooField.myRev, this::serdeSerialize);
130+
// from
131+
testSerialize(fooField, "myFrom", fooField.myFrom, this::jacksonSerialize);
132+
testSerialize(fooField, "_from", fooField.myFrom, this::serdeSerialize);
133+
// to
134+
testSerialize(fooField, "myTo", fooField.myTo, this::jacksonSerialize);
135+
testSerialize(fooField, "_to", fooField.myTo, this::serdeSerialize);
136+
}
137+
138+
@Test
139+
void serializeProp() {
140+
// id
141+
testSerialize(fooProp, "myId", fooProp.myId, this::jacksonSerialize);
142+
testSerialize(fooProp, "_id", fooProp.myId, this::serdeSerialize);
143+
// key
144+
testSerialize(fooProp, "myKey", fooProp.myKey, this::jacksonSerialize);
145+
testSerialize(fooProp, "_key", fooProp.myKey, this::serdeSerialize);
146+
// rev
147+
testSerialize(fooProp, "myRev", fooProp.myRev, this::jacksonSerialize);
148+
testSerialize(fooProp, "_rev", fooProp.myRev, this::serdeSerialize);
149+
// from
150+
testSerialize(fooProp, "myFrom", fooProp.myFrom, this::jacksonSerialize);
151+
testSerialize(fooProp, "_from", fooProp.myFrom, this::serdeSerialize);
152+
// to
153+
testSerialize(fooProp, "myTo", fooProp.myTo, this::jacksonSerialize);
154+
testSerialize(fooProp, "_to", fooProp.myTo, this::serdeSerialize);
155+
}
156+
157+
@Test
158+
void deserializeField() throws IOException {
159+
// id
160+
testDeserialize("myId", FooField.class, foo -> foo.myId, this::jacksonDeserialize);
161+
testDeserialize("_id", FooField.class, foo -> foo.myId, this::serdeDeserialize);
162+
// key
163+
testDeserialize("myKey", FooField.class, foo -> foo.myKey, this::jacksonDeserialize);
164+
testDeserialize("_key", FooField.class, foo -> foo.myKey, this::serdeDeserialize);
165+
// rev
166+
testDeserialize("myRev", FooField.class, foo -> foo.myRev, this::jacksonDeserialize);
167+
testDeserialize("_rev", FooField.class, foo -> foo.myRev, this::serdeDeserialize);
168+
// from
169+
testDeserialize("myFrom", FooField.class, foo -> foo.myFrom, this::jacksonDeserialize);
170+
testDeserialize("_from", FooField.class, foo -> foo.myFrom, this::serdeDeserialize);
171+
// to
172+
testDeserialize("myTo", FooField.class, foo -> foo.myTo, this::jacksonDeserialize);
173+
testDeserialize("_to", FooField.class, foo -> foo.myTo, this::serdeDeserialize);
174+
}
175+
176+
@Test
177+
void deserializeProp() throws IOException {
178+
// id
179+
testDeserialize("myId", FooProp.class, FooProp::getMyId, this::jacksonDeserialize);
180+
testDeserialize("_id", FooProp.class, FooProp::getMyId, this::serdeDeserialize);
181+
// key
182+
testDeserialize("myKey", FooProp.class, FooProp::getMyKey, this::jacksonDeserialize);
183+
testDeserialize("_key", FooProp.class, FooProp::getMyKey, this::serdeDeserialize);
184+
// rev
185+
testDeserialize("myRev", FooProp.class, FooProp::getMyRev, this::jacksonDeserialize);
186+
testDeserialize("_rev", FooProp.class, FooProp::getMyRev, this::serdeDeserialize);
187+
// from
188+
testDeserialize("myFrom", FooProp.class, FooProp::getMyFrom, this::jacksonDeserialize);
189+
testDeserialize("_from", FooProp.class, FooProp::getMyFrom, this::serdeDeserialize);
190+
// to
191+
testDeserialize("myTo", FooProp.class, FooProp::getMyTo, this::jacksonDeserialize);
192+
testDeserialize("_to", FooProp.class, FooProp::getMyTo, this::serdeDeserialize);
193+
}
194+
195+
void testSerialize(Object data, String fieldName, String expectedValue, Function<Object, JsonNode> serializer) {
196+
JsonNode jn = serializer.apply(data).get(fieldName);
197+
assertThat(jn).isNotNull();
198+
assertThat(jn.textValue()).isEqualTo(expectedValue);
199+
}
200+
201+
<T> void testDeserialize(String fieldName, Class<T> clazz, Function<T, String> getter,
202+
BiFunction<byte[], Class<T>, T> deserializer) throws IOException {
203+
String fieldValue = UUID.randomUUID().toString();
204+
ObjectNode on = JsonNodeFactory.instance.objectNode().put(fieldName, fieldValue);
205+
byte[] bytes = mapper.writeValueAsBytes(on);
206+
T deser = deserializer.apply(bytes, clazz);
207+
assertThat(getter.apply(deser)).isEqualTo(fieldValue);
208+
}
209+
210+
private JsonNode jacksonSerialize(Object data) {
211+
try {
212+
return mapper.readTree(mapper.writeValueAsBytes(data));
213+
} catch (IOException e) {
214+
throw new RuntimeException(e);
215+
}
216+
}
217+
218+
private JsonNode serdeSerialize(Object data) {
219+
try {
220+
return mapper.readTree(serde.serialize(data));
221+
} catch (IOException e) {
222+
throw new RuntimeException(e);
223+
}
224+
}
225+
226+
private <T> T jacksonDeserialize(byte[] bytes, Class<T> clazz) {
227+
try {
228+
return mapper.readValue(bytes, clazz);
229+
} catch (IOException e) {
230+
throw new RuntimeException(e);
231+
}
232+
}
233+
234+
private <T> T serdeDeserialize(byte[] bytes, Class<T> clazz) {
235+
return serde.deserialize(bytes, clazz);
236+
}
237+
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.arangodb.serde.jackson;
22

3-
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
6-
73
import java.lang.annotation.ElementType;
84
import java.lang.annotation.Retention;
95
import java.lang.annotation.RetentionPolicy;
@@ -14,8 +10,5 @@
1410
*/
1511
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1612
@Retention(RetentionPolicy.RUNTIME)
17-
@JacksonAnnotationsInside
18-
@JsonProperty("_from")
19-
@JsonInclude(JsonInclude.Include.NON_NULL)
2013
public @interface From {
2114
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.arangodb.serde.jackson;
22

3-
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
6-
73
import java.lang.annotation.ElementType;
84
import java.lang.annotation.Retention;
95
import java.lang.annotation.RetentionPolicy;
@@ -14,8 +10,5 @@
1410
*/
1511
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1612
@Retention(RetentionPolicy.RUNTIME)
17-
@JacksonAnnotationsInside
18-
@JsonProperty("_id")
19-
@JsonInclude(JsonInclude.Include.NON_NULL)
2013
public @interface Id {
2114
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.arangodb.serde.jackson;
22

3-
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
6-
73
import java.lang.annotation.ElementType;
84
import java.lang.annotation.Retention;
95
import java.lang.annotation.RetentionPolicy;
@@ -14,8 +10,5 @@
1410
*/
1511
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1612
@Retention(RetentionPolicy.RUNTIME)
17-
@JacksonAnnotationsInside
18-
@JsonProperty("_key")
19-
@JsonInclude(JsonInclude.Include.NON_NULL)
2013
public @interface Key {
2114
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.arangodb.serde.jackson;
22

3-
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
6-
73
import java.lang.annotation.ElementType;
84
import java.lang.annotation.Retention;
95
import java.lang.annotation.RetentionPolicy;
@@ -14,8 +10,5 @@
1410
*/
1511
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1612
@Retention(RetentionPolicy.RUNTIME)
17-
@JacksonAnnotationsInside
18-
@JsonProperty("_rev")
19-
@JsonInclude(JsonInclude.Include.NON_NULL)
2013
public @interface Rev {
2114
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.arangodb.serde.jackson;
22

3-
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
6-
73
import java.lang.annotation.ElementType;
84
import java.lang.annotation.Retention;
95
import java.lang.annotation.RetentionPolicy;
@@ -14,8 +10,5 @@
1410
*/
1511
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1612
@Retention(RetentionPolicy.RUNTIME)
17-
@JacksonAnnotationsInside
18-
@JsonProperty("_to")
19-
@JsonInclude(JsonInclude.Include.NON_NULL)
2013
public @interface To {
2114
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.arangodb.serde.jackson.internal;
2+
3+
import com.arangodb.serde.jackson.*;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.databind.PropertyName;
6+
import com.fasterxml.jackson.databind.introspect.Annotated;
7+
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
8+
9+
import java.lang.annotation.Annotation;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
import java.util.Optional;
13+
14+
class ArangoSerdeAnnotationIntrospector extends JacksonAnnotationIntrospector {
15+
private static final JsonInclude JSON_INCLUDE_NON_NULL = JsonIncludeNonNull.class.getAnnotation(JsonInclude.class);
16+
private static final Map<Class<? extends Annotation>, String> MAPPINGS;
17+
private static final Class<? extends Annotation>[] ANNOTATIONS;
18+
19+
static {
20+
MAPPINGS = new HashMap<>();
21+
MAPPINGS.put(Id.class, "_id");
22+
MAPPINGS.put(Key.class, "_key");
23+
MAPPINGS.put(Rev.class, "_rev");
24+
MAPPINGS.put(From.class, "_from");
25+
MAPPINGS.put(To.class, "_to");
26+
ANNOTATIONS = MAPPINGS.keySet().toArray(new Class[0]);
27+
}
28+
29+
@JsonInclude(JsonInclude.Include.NON_NULL)
30+
private static class JsonIncludeNonNull {
31+
}
32+
33+
@Override
34+
public PropertyName findNameForSerialization(Annotated a) {
35+
return Optional.ofNullable(findMapping(a)).orElseGet(() -> super.findNameForSerialization(a));
36+
}
37+
38+
@Override
39+
public PropertyName findNameForDeserialization(Annotated a) {
40+
return Optional.ofNullable(findMapping(a)).orElseGet(() -> super.findNameForDeserialization(a));
41+
}
42+
43+
private PropertyName findMapping(Annotated a) {
44+
for (Map.Entry<Class<? extends Annotation>, String> e : MAPPINGS.entrySet()) {
45+
if (_hasAnnotation(a, e.getKey())) {
46+
return PropertyName.construct(e.getValue());
47+
}
48+
}
49+
return null;
50+
}
51+
52+
@Override
53+
public JsonInclude.Value findPropertyInclusion(Annotated a) {
54+
if (_hasOneOf(a, ANNOTATIONS)) {
55+
return new JsonInclude.Value(JSON_INCLUDE_NON_NULL);
56+
} else {
57+
return super.findPropertyInclusion(a);
58+
}
59+
}
60+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /