From 31ea06925d1753eb887bbcef712003db99c6c6a5 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 13 Jan 2022 08:37:09 +0100 Subject: [PATCH 1/2] Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c38669587..4678b59d32 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 2.7.0-SNAPSHOT + 2.7.0-GH-1566-SNAPSHOT Spring Data Redis From ff7be60302140b202431d9412f8806030b27d8d3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 13 Jan 2022 10:13:51 +0100 Subject: [PATCH 2/2] Switch jackson default mapping default from NON_FINAL to EVERYTHING. --- .../data/redis/hash/Jackson2HashMapper.java | 31 +++++++++++++++++-- .../GenericJackson2JsonRedisSerializer.java | 11 +++++-- .../mapping/Jackson2HashMapperUnitTests.java | 14 +++++++++ ...cJackson2JsonRedisSerializerUnitTests.java | 23 ++++++++++++-- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index b6652b4f39..44a5382c4f 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -15,6 +15,8 @@ */ package org.springframework.data.redis.hash; +import static com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.*; + import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; @@ -39,8 +41,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; @@ -49,7 +53,9 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; +import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.CalendarSerializer; @@ -157,9 +163,30 @@ public class Jackson2HashMapper implements HashMapper { */ public Jackson2HashMapper(boolean flatten) { - this(new ObjectMapper().findAndRegisterModules(), flatten); + this(new ObjectMapper() { + + @Override + protected TypeResolverBuilder _constructDefaultTypeResolverBuilder(DefaultTyping applicability, + PolymorphicTypeValidator ptv) { + return new DefaultTypeResolverBuilder(applicability, ptv) { + public boolean useForType(JavaType t) { + + if (t.isPrimitive()) { + return false; + } + + if (EVERYTHING.equals(_appliesFor)) { + return !TreeNode.class.isAssignableFrom(t.getRawClass()); + } + + return super.useForType(t); + } + }; + } + }.findAndRegisterModules(), flatten); - typingMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); + typingMapper.activateDefaultTyping(typingMapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING, + As.PROPERTY); typingMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); // Prevent splitting time types into arrays. E 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 4535b87edd..b1296caedb 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -30,6 +30,7 @@ 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.module.SimpleModule; import com.fasterxml.jackson.databind.ser.SerializerFactory; import com.fasterxml.jackson.databind.ser.std.StdSerializer; @@ -71,10 +72,10 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName registerNullValueSerializer(mapper, classPropertyTypeName); if (StringUtils.hasText(classPropertyTypeName)) { - mapper.activateDefaultTypingAsProperty(mapper.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL, + mapper.activateDefaultTypingAsProperty(mapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING, classPropertyTypeName); } else { - mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL, As.PROPERTY); + mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING, As.PROPERTY); } } @@ -190,5 +191,11 @@ public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider pr jgen.writeStringField(classIdentifier, NullValue.class.getName()); jgen.writeEndObject(); } + + @Override + public void serializeWithType(NullValue value, JsonGenerator gen, SerializerProvider serializers, + TypeSerializer typeSer) throws IOException { + serialize(value, gen, serializers); + } } } diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java index 22e3f20a0f..7553c4621a 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java @@ -183,6 +183,15 @@ void dateValueShouldBeTreatedCorrectly() { assertBackAndForwardMapping(source); } + @Test // GH-1566 + void mapFinalClass() { + + MeFinal source = new MeFinal(); + source.value = "id-1"; + + assertBackAndForwardMapping(source); + } + @Data public static class WithList { List strings; @@ -206,4 +215,9 @@ private static class WithDates { private LocalDate localDate; private LocalDateTime localDateTime; } + + @Data + public static final class MeFinal { + private String value; + } } 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 56e7959257..ed1d5cb30f 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -20,11 +20,12 @@ import static org.springframework.test.util.ReflectionTestUtils.*; import static org.springframework.util.ObjectUtils.*; +import lombok.Data; + import java.io.IOException; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - import org.springframework.beans.BeanUtils; import org.springframework.cache.support.NullValue; @@ -141,7 +142,7 @@ void shouldSerializeNullValueSoThatItCanBeDeserializedWithDefaultTypingEnabled() void shouldSerializeNullValueWithCustomObjectMapper() { ObjectMapper mapper = new ObjectMapper(); - mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); + mapper.enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY); GenericJackson2JsonRedisSerializer.registerNullValueSerializer(mapper, null); GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(mapper); @@ -149,6 +150,18 @@ void shouldSerializeNullValueWithCustomObjectMapper() { serializeAndDeserializeNullValue(serializer); } + @Test // GH-1566 + void deserializeShouldBeAbleToRestoreFinalObjectAfterSerialization() { + + GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(); + + FinalObject source = new FinalObject(); + source.longValue = 1L; + source.simpleObject = new SimpleObject(2L); + + assertThat(serializer.deserialize(serializer.serialize(source))).isEqualTo(source); + } + private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { NullValue nv = BeanUtils.instantiateClass(NullValue.class); @@ -201,6 +214,12 @@ public boolean equals(Object obj) { } + @Data + static final class FinalObject { + public Long longValue; + SimpleObject simpleObject; + } + static class SimpleObject { public Long longValue;