Skip to content

Commit ea6298b

Browse files
Merge pull request #43 from gravity9-tech/feature/set-custom-object-mapper-for-merge-patch
Allow users to customise the ObjectMapper used in JsonMergePatch deserialization
2 parents 3dbae22 + 729f26b commit ea6298b

File tree

5 files changed

+53
-43
lines changed

5 files changed

+53
-43
lines changed

src/main/java/com/gravity9/jsonpatch/mergepatch/JsonMergePatch.java

+19-6
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@
6565
@JsonDeserialize(using = JsonMergePatchDeserializer.class)
6666
public abstract class JsonMergePatch implements JsonSerializable, Patch {
6767

68-
protected static final MessageBundle BUNDLE
69-
= MessageBundles.getBundle(JsonPatchMessages.class);
70-
private static final ObjectMapper MAPPER = JacksonUtils.newMapper();
68+
protected static final MessageBundle BUNDLE = MessageBundles.getBundle(JsonPatchMessages.class);
69+
private static final ObjectMapper DEFAULT_MAPPER = JacksonUtils.newMapper();
7170

7271
/**
7372
* Build an instance from a JSON input
@@ -77,11 +76,25 @@ public abstract class JsonMergePatch implements JsonSerializable, Patch {
7776
* @throws JsonPatchException failed to deserialize
7877
* @throws NullPointerException node is null
7978
*/
80-
public static JsonMergePatch fromJson(final JsonNode node)
81-
throws JsonPatchException {
79+
public static JsonMergePatch fromJson(final JsonNode node) throws JsonPatchException {
80+
return fromJson(node, DEFAULT_MAPPER);
81+
}
82+
83+
/**
84+
* Build an instance from a JSON input with a custom ObjectMapper.
85+
* This allows to customize the mapper used in deserialization of nodes.
86+
*
87+
* @param node the input
88+
* @param mapper custom ObjectMapper
89+
* @return a JSON Merge Patch instance
90+
* @throws JsonPatchException failed to deserialize
91+
* @throws NullPointerException node or mapper is null
92+
*/
93+
public static JsonMergePatch fromJson(final JsonNode node, ObjectMapper mapper) throws JsonPatchException {
8294
BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
95+
BUNDLE.checkNotNull(mapper, "jsonPatch.nullInput");
8396
try {
84-
return MAPPER.readValue(node.traverse(), JsonMergePatch.class);
97+
return mapper.readValue(node.traverse(), JsonMergePatch.class);
8598
} catch (IOException e) {
8699
throw new JsonPatchException(
87100
BUNDLE.getMessage("jsonPatch.deserFailed"), e);

src/main/java/com/gravity9/jsonpatch/mergepatch/JsonMergePatchDeserializer.java

+8-20
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@
2020
package com.gravity9.jsonpatch.mergepatch;
2121

2222
import com.fasterxml.jackson.core.JsonParser;
23-
import com.fasterxml.jackson.core.JsonProcessingException;
24-
import com.fasterxml.jackson.core.ObjectCodec;
2523
import com.fasterxml.jackson.databind.DeserializationContext;
2624
import com.fasterxml.jackson.databind.JsonDeserializer;
2725
import com.fasterxml.jackson.databind.JsonNode;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
2827
import com.fasterxml.jackson.databind.node.NullNode;
29-
import com.github.fge.jackson.JacksonUtils;
3028
import java.io.IOException;
3129
import java.util.HashMap;
3230
import java.util.HashSet;
@@ -35,30 +33,20 @@
3533
import java.util.Set;
3634

3735
final class JsonMergePatchDeserializer extends JsonDeserializer<JsonMergePatch> {
38-
/*
39-
* FIXME! UGLY! HACK!
40-
*
41-
* We MUST have an ObjectCodec ready so that the parser in .deserialize()
42-
* can actually do something useful -- for instance, deserializing even a
43-
* JsonNode.
44-
*
45-
* Jackson does not do this automatically; I don't know why...
46-
*/
47-
private static final ObjectCodec CODEC = JacksonUtils.newMapper();
4836

4937
@Override
5038
public JsonMergePatch deserialize(final JsonParser jp,
51-
final DeserializationContext ctxt)
52-
throws IOException, JsonProcessingException {
53-
// FIXME: see comment above
54-
jp.setCodec(CODEC);
39+
final DeserializationContext ctxt) throws IOException {
40+
ObjectMapper mapper = new ObjectMapper().setConfig(ctxt.getConfig());
41+
jp.setCodec(mapper);
5542
final JsonNode node = jp.readValueAsTree();
5643

5744
/*
5845
* Not an object: the simple case
5946
*/
60-
if (!node.isObject())
47+
if (!node.isObject()) {
6148
return new NonObjectMergePatch(node);
49+
}
6250

6351
/*
6452
* The complicated case...
@@ -67,8 +55,8 @@ public JsonMergePatch deserialize(final JsonParser jp,
6755
* members.
6856
*/
6957

70-
final Set<String> removedMembers = new HashSet<String>();
71-
final Map<String, JsonMergePatch> modifiedMembers = new HashMap<String, JsonMergePatch>();
58+
final Set<String> removedMembers = new HashSet<>();
59+
final Map<String, JsonMergePatch> modifiedMembers = new HashMap<>();
7260
final Iterator<Map.Entry<String, JsonNode>> iterator = node.fields();
7361

7462
Map.Entry<String, JsonNode> entry;

src/main/java/com/gravity9/jsonpatch/mergepatch/NonObjectMergePatch.java

+4-8
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
package com.gravity9.jsonpatch.mergepatch;
2121

2222
import com.fasterxml.jackson.core.JsonGenerator;
23-
import com.fasterxml.jackson.core.JsonProcessingException;
2423
import com.fasterxml.jackson.databind.JsonNode;
2524
import com.fasterxml.jackson.databind.SerializerProvider;
2625
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
27-
import com.gravity9.jsonpatch.JsonPatchException;
2826
import java.io.IOException;
2927
import javax.annotation.ParametersAreNonnullByDefault;
3028

@@ -41,23 +39,21 @@ final class NonObjectMergePatch extends JsonMergePatch {
4139
}
4240

4341
@Override
44-
public JsonNode apply(final JsonNode input)
45-
throws JsonPatchException {
42+
public JsonNode apply(final JsonNode input) {
4643
BUNDLE.checkNotNull(input, "jsonPatch.nullValue");
4744
return node;
4845
}
4946

5047
@Override
5148
public void serialize(final JsonGenerator jgen,
52-
final SerializerProvider provider)
53-
throws IOException, JsonProcessingException {
49+
final SerializerProvider provider) throws IOException {
5450
jgen.writeTree(node);
5551
}
5652

5753
@Override
5854
public void serializeWithType(final JsonGenerator jgen,
59-
final SerializerProvider provider, final TypeSerializer typeSer)
60-
throws IOException, JsonProcessingException {
55+
final SerializerProvider provider,
56+
final TypeSerializer typeSer) throws IOException {
6157
serialize(jgen, provider);
6258
}
6359
}

src/main/java/com/gravity9/jsonpatch/mergepatch/ObjectMergePatch.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package com.gravity9.jsonpatch.mergepatch;
2121

2222
import com.fasterxml.jackson.core.JsonGenerator;
23-
import com.fasterxml.jackson.core.JsonProcessingException;
2423
import com.fasterxml.jackson.databind.JsonNode;
2524
import com.fasterxml.jackson.databind.SerializerProvider;
2625
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -44,13 +43,12 @@ final class ObjectMergePatch extends JsonMergePatch {
4443

4544
ObjectMergePatch(final Set<String> removedMembers,
4645
final Map<String, JsonMergePatch> modifiedMembers) {
47-
this.removedMembers = Collections.unmodifiableSet(new HashSet<String>(removedMembers));
48-
this.modifiedMembers = Collections.unmodifiableMap(new HashMap<String, JsonMergePatch>(modifiedMembers));
46+
this.removedMembers = Collections.unmodifiableSet(new HashSet<>(removedMembers));
47+
this.modifiedMembers = Collections.unmodifiableMap(new HashMap<>(modifiedMembers));
4948
}
5049

5150
@Override
52-
public JsonNode apply(final JsonNode input)
53-
throws JsonPatchException {
51+
public JsonNode apply(final JsonNode input) throws JsonPatchException {
5452
BUNDLE.checkNotNull(input, "jsonPatch.nullValue");
5553
/*
5654
* If the input is an object, we make a deep copy of it
@@ -90,8 +88,7 @@ public JsonNode apply(final JsonNode input)
9088

9189
@Override
9290
public void serialize(final JsonGenerator jgen,
93-
final SerializerProvider provider)
94-
throws IOException, JsonProcessingException {
91+
final SerializerProvider provider) throws IOException {
9592
jgen.writeStartObject();
9693

9794
/*
@@ -114,8 +111,8 @@ public void serialize(final JsonGenerator jgen,
114111

115112
@Override
116113
public void serializeWithType(final JsonGenerator jgen,
117-
final SerializerProvider provider, final TypeSerializer typeSer)
118-
throws IOException, JsonProcessingException {
114+
final SerializerProvider provider,
115+
final TypeSerializer typeSer) throws IOException {
119116
serialize(jgen, provider);
120117
}
121118
}

src/test/java/com/gravity9/jsonpatch/mergepatch/SerializationTest.java

+16
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
package com.gravity9.jsonpatch.mergepatch;
2121

22+
import com.fasterxml.jackson.databind.DeserializationFeature;
2223
import com.fasterxml.jackson.databind.JsonNode;
2324
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
2426
import com.github.fge.jackson.JacksonUtils;
2527
import com.github.fge.jackson.JsonLoader;
2628
import com.github.fge.jackson.JsonNumEquals;
@@ -34,6 +36,7 @@
3436
import static org.testng.Assert.assertNotNull;
3537
import static org.testng.Assert.assertSame;
3638
import static org.testng.Assert.assertTrue;
39+
import static org.testng.AssertJUnit.assertEquals;
3740

3841
public final class SerializationTest {
3942

@@ -102,4 +105,17 @@ public void objectSerDeserWorksCorrectly(final JsonNode input)
102105

103106
assertTrue(EQUIVALENCE.equivalent(input, serialized));
104107
}
108+
109+
@Test
110+
void testDecimalsKeptAfterPatchWithCustomObjectMapper() throws Exception {
111+
ObjectMapper mapper = new ObjectMapper()
112+
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
113+
.setNodeFactory(new JsonNodeFactory(true));
114+
115+
JsonNode newer = mapper.readTree("25.000");
116+
JsonNode older = mapper.readTree("24.444");
117+
JsonNode apply = JsonMergePatch.fromJson(newer, mapper).apply(older);
118+
119+
assertEquals("25.000", apply.asText());
120+
}
105121
}

0 commit comments

Comments
 (0)