Skip to content

Commit 8c74d78

Browse files
mp911dechristophstrobl
authored andcommitted
Support deserialization in GenericJackson2JsonRedisSerializer when using custom JsonFactory.
Closes: #2981 Original Pull Request: #2999
1 parent f2752d1 commit 8c74d78

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@
276276
<scope>test</scope>
277277
</dependency>
278278

279+
<dependency>
280+
<groupId>org.msgpack</groupId>
281+
<artifactId>jackson-dataformat-msgpack</artifactId>
282+
<version>0.9.8</version>
283+
<scope>test</scope>
284+
</dependency>
285+
279286
<dependency>
280287
<groupId>edu.umd.cs.mtc</groupId>
281288
<artifactId>multithreadedtc</artifactId>

src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java

+47-6
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,19 @@
3232
import com.fasterxml.jackson.annotation.JsonTypeInfo;
3333
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
3434
import com.fasterxml.jackson.core.JsonGenerator;
35+
import com.fasterxml.jackson.core.JsonParser;
36+
import com.fasterxml.jackson.core.JsonToken;
3537
import com.fasterxml.jackson.core.TreeNode;
3638
import com.fasterxml.jackson.databind.DeserializationConfig;
3739
import com.fasterxml.jackson.databind.JavaType;
40+
import com.fasterxml.jackson.databind.JsonDeserializer;
3841
import com.fasterxml.jackson.databind.JsonNode;
3942
import com.fasterxml.jackson.databind.ObjectMapper;
4043
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
4144
import com.fasterxml.jackson.databind.SerializerProvider;
45+
import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory;
46+
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
47+
import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer;
4248
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
4349
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
4450
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
@@ -179,7 +185,7 @@ private static TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable Strin
179185
Lazy<String> lazyTypeHintPropertyName = typeHintPropertyName != null ? Lazy.of(typeHintPropertyName)
180186
: newLazyTypeHintPropertyName(mapper, defaultTypingEnabled);
181187

182-
return new TypeResolver(lazyTypeFactory, lazyTypeHintPropertyName);
188+
return new TypeResolver(mapper, lazyTypeFactory, lazyTypeHintPropertyName);
183189
}
184190

185191
private static Lazy<String> newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy<Boolean> defaultTypingEnabled) {
@@ -340,14 +346,13 @@ protected JavaType resolveType(byte[] source, Class<?> type) throws IOException
340346
*/
341347
static class TypeResolver {
342348

343-
// need a separate instance to bypass class hint checks
344-
private final ObjectMapper mapper = new ObjectMapper();
345-
349+
private final ObjectMapper mapper;
346350
private final Supplier<TypeFactory> typeFactory;
347351
private final Supplier<String> hintName;
348352

349-
TypeResolver(Supplier<TypeFactory> typeFactory, Supplier<String> hintName) {
353+
TypeResolver(ObjectMapper mapper, Supplier<TypeFactory> typeFactory, Supplier<String> hintName) {
350354

355+
this.mapper = mapper;
351356
this.typeFactory = typeFactory;
352357
this.hintName = hintName;
353358
}
@@ -358,7 +363,7 @@ protected JavaType constructType(Class<?> type) {
358363

359364
protected JavaType resolveType(byte[] source, Class<?> type) throws IOException {
360365

361-
JsonNode root = mapper.readTree(source);
366+
JsonNode root = readTree(source);
362367
JsonNode jsonNode = root.get(hintName.get());
363368

364369
if (jsonNode instanceof TextNode && jsonNode.asText() != null) {
@@ -367,6 +372,42 @@ protected JavaType resolveType(byte[] source, Class<?> type) throws IOException
367372

368373
return constructType(type);
369374
}
375+
376+
/**
377+
* Lenient variant of ObjectMapper._readTreeAndClose using a strict {@link JsonNodeDeserializer}.
378+
*
379+
* @param source
380+
* @return
381+
* @throws IOException
382+
*/
383+
private JsonNode readTree(byte[] source) throws IOException {
384+
385+
JsonDeserializer<? extends JsonNode> deserializer = JsonNodeDeserializer.getDeserializer(JsonNode.class);
386+
DeserializationConfig cfg = mapper.getDeserializationConfig();
387+
388+
try (JsonParser parser = mapper.createParser(source)) {
389+
390+
cfg.initialize(parser);
391+
JsonToken t = parser.currentToken();
392+
if (t == null) {
393+
t = parser.nextToken();
394+
if (t == null) {
395+
return cfg.getNodeFactory().missingNode();
396+
}
397+
}
398+
399+
/*
400+
* Hokey pokey! Oh my.
401+
*/
402+
DefaultDeserializationContext ctxt = new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance)
403+
.createInstance(cfg, parser, mapper.getInjectableValues());
404+
if (t == JsonToken.VALUE_NULL) {
405+
return cfg.getNodeFactory().nullNode();
406+
} else {
407+
return deserializer.deserialize(parser, ctxt);
408+
}
409+
}
410+
}
370411
}
371412

372413
/**

src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java

+27
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import org.junit.jupiter.api.Test;
3434
import org.mockito.Mockito;
35+
import org.msgpack.jackson.dataformat.MessagePackFactory;
3536

3637
import org.springframework.beans.BeanUtils;
3738
import org.springframework.cache.support.NullValue;
@@ -449,6 +450,7 @@ void configureWithNullConsumerThrowsIllegalArgumentException() {
449450

450451
@Test
451452
void defaultSerializeAndDeserializeNullValueWithBuilderClass() {
453+
452454
GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder()
453455
.objectMapper(new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY))
454456
.build();
@@ -487,6 +489,31 @@ public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, Seri
487489
assertThat(deserializedValue).isNull();
488490
}
489491

492+
@Test // GH-2981
493+
void defaultSerializeAndDeserializeWithCustomJsonFactory() {
494+
495+
GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder()
496+
.objectMapper(
497+
new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY))
498+
.build();
499+
500+
byte[] serializedValue = serializer.serialize(COMPLEX_OBJECT);
501+
502+
Object deserializedValue = serializer.deserialize(serializedValue, Object.class);
503+
assertThat(deserializedValue).isEqualTo(COMPLEX_OBJECT);
504+
}
505+
506+
@Test // GH-2981
507+
void defaultSerializeAndDeserializeNullValueWithBuilderClassAndCustomJsonFactory() {
508+
509+
GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder()
510+
.objectMapper(
511+
new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY))
512+
.build();
513+
514+
serializeAndDeserializeNullValue(serializer);
515+
}
516+
490517
private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) {
491518

492519
NullValue nv = BeanUtils.instantiateClass(NullValue.class);

0 commit comments

Comments
 (0)