From 0e23a805cab15a87f537006c1c5db6c655d60327 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 13 Jul 2022 16:05:02 +0200 Subject: [PATCH] Introduce custom StdTypeResolverBuilder to support primitive arrays without type hints. Closes: #2361 Original Pull Request: #2364 --- .../GenericJackson2JsonRedisSerializer.java | 66 +++++++++++++++++-- ...cJackson2JsonRedisSerializerUnitTests.java | 25 +++++++ 2 files changed, 85 insertions(+), 6 deletions(-) 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 b1296caed..00423c4d0 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -20,17 +20,21 @@ import java.io.IOException; import org.springframework.cache.support.NullValue; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; 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.JsonProcessingException; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.SerializerFactory; import com.fasterxml.jackson.databind.ser.std.StdSerializer; @@ -71,12 +75,15 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer defaultImpl) { + return this; + } + + /** + * Method called to check if the default type handler should be used for given type. Note: "natural types" (String, + * Boolean, Integer, Double) will never use typing; that is both due to them being concrete and final, and since + * actual serializers and deserializers will also ignore any attempts to enforce typing. + */ + public boolean useForType(JavaType t) { + + if (t.isJavaLangObject()) { + return true; + } + + while (t.isArrayType()) { + t = t.getContentType(); + } + + if (ClassUtils.isPrimitiveOrWrapper(t.getRawClass())) { + return false; + } + + // 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling: + while (t.isReferenceType()) { + t = t.getReferencedType(); + } + + // [databind#88] Should not apply to JSON tree models: + return !TreeNode.class.isAssignableFrom(t.getRawClass()); + } + } } 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 f13d7cf1a..890cee5c0 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -157,9 +157,26 @@ class GenericJackson2JsonRedisSerializerUnitTests { FinalObject source = new FinalObject(); source.longValue = 1L; + source.myArray = new int[] { 1, 2, 3 }; source.simpleObject = new SimpleObject(2L); assertThat(serializer.deserialize(serializer.serialize(source))).isEqualTo(source); + assertThat(serializer.deserialize( + ("{\"@class\":\"org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializerUnitTests$FinalObject\",\"longValue\":1,\"myArray\":[1,2,3],\n" + + "\"simpleObject\":{\"@class\":\"org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializerUnitTests$SimpleObject\",\"longValue\":2}}") + .getBytes())).isEqualTo(source); + } + + @Test // GH-2361 + void shouldDeserializeArrayWithoutTypeHint() { + + GenericJackson2JsonRedisSerializer gs = new GenericJackson2JsonRedisSerializer(); + CountAndArray result = (CountAndArray) gs.deserialize( + ("{\"@class\":\"org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializerUnitTests$CountAndArray\", \"count\":1, \"available\":[0,1]}") + .getBytes()); + + assertThat(result.getCount()).isEqualTo(1); + assertThat(result.getAvailable()).containsExactly(0, 1); } private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { @@ -217,6 +234,7 @@ class GenericJackson2JsonRedisSerializerUnitTests { @Data static final class FinalObject { public Long longValue; + public int[] myArray; SimpleObject simpleObject; } @@ -252,4 +270,11 @@ class GenericJackson2JsonRedisSerializerUnitTests { } } + @Data + static class CountAndArray { + + private int count; + private int[] available; + } + }