diff --git a/pom.xml b/pom.xml index 09bebeb5b..225297442 100644 --- a/pom.xml +++ b/pom.xml @@ -276,6 +276,13 @@ test + + org.msgpack + jackson-dataformat-msgpack + 0.9.8 + test + + edu.umd.cs.mtc multithreadedtc diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 7ecc89d18..9358f36a7 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -32,13 +32,19 @@ import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory; +import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; +import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer; import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; @@ -179,7 +185,7 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer lazyTypeHintPropertyName = typeHintPropertyName != null ? Lazy.of(typeHintPropertyName) : newLazyTypeHintPropertyName(mapper, defaultTypingEnabled); - return new TypeResolver(lazyTypeFactory, lazyTypeHintPropertyName); + return new TypeResolver(mapper, lazyTypeFactory, lazyTypeHintPropertyName); } private static Lazy newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy defaultTypingEnabled) { @@ -340,14 +346,13 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer typeFactory; private final Supplier hintName; - TypeResolver(Supplier typeFactory, Supplier hintName) { + TypeResolver(ObjectMapper mapper, Supplier typeFactory, Supplier hintName) { + this.mapper = mapper; this.typeFactory = typeFactory; this.hintName = hintName; } @@ -358,7 +363,7 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer type) throws IOException { - JsonNode root = mapper.readTree(source); + JsonNode root = readTree(source); JsonNode jsonNode = root.get(hintName.get()); if (jsonNode instanceof TextNode && jsonNode.asText() != null) { @@ -367,6 +372,42 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer deserializer = JsonNodeDeserializer.getDeserializer(JsonNode.class); + DeserializationConfig cfg = mapper.getDeserializationConfig(); + + try (JsonParser parser = mapper.createParser(source)) { + + cfg.initialize(parser); + JsonToken t = parser.currentToken(); + if (t == null) { + t = parser.nextToken(); + if (t == null) { + return cfg.getNodeFactory().missingNode(); + } + } + + /* + * Hokey pokey! Oh my. + */ + DefaultDeserializationContext ctxt = new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance) + .createInstance(cfg, parser, mapper.getInjectableValues()); + if (t == JsonToken.VALUE_NULL) { + return cfg.getNodeFactory().nullNode(); + } else { + return deserializer.deserialize(parser, ctxt); + } + } + } } /** diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index 16daa677c..c91e6b46f 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -32,6 +32,7 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.msgpack.jackson.dataformat.MessagePackFactory; import org.springframework.beans.BeanUtils; import org.springframework.cache.support.NullValue; @@ -449,6 +450,7 @@ class GenericJackson2JsonRedisSerializerUnitTests { @Test void defaultSerializeAndDeserializeNullValueWithBuilderClass() { + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() .objectMapper(new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) .build(); @@ -487,6 +489,31 @@ class GenericJackson2JsonRedisSerializerUnitTests { assertThat(deserializedValue).isNull(); } + @Test // GH-2981 + void defaultSerializeAndDeserializeWithCustomJsonFactory() { + + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper( + new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) + .build(); + + byte[] serializedValue = serializer.serialize(COMPLEX_OBJECT); + + Object deserializedValue = serializer.deserialize(serializedValue, Object.class); + assertThat(deserializedValue).isEqualTo(COMPLEX_OBJECT); + } + + @Test // GH-2981 + void defaultSerializeAndDeserializeNullValueWithBuilderClassAndCustomJsonFactory() { + + GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder() + .objectMapper( + new ObjectMapper(new MessagePackFactory()).enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY)) + .build(); + + serializeAndDeserializeNullValue(serializer); + } + private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { NullValue nv = BeanUtils.instantiateClass(NullValue.class);