Switch Jackson default mapping default from NON_FINAL to EVERYTHING.

Closes #1566
Original pull request: #2237.
This commit is contained in:
Christoph Strobl
2022-01-13 10:13:51 +01:00
committed by Mark Paluch
parent e53a5762b1
commit 93c25b0608
4 changed files with 73 additions and 6 deletions

View File

@@ -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.JsonInclude.Include;
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.ObjectMapper.DefaultTyping;
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<Object, String, Object> {
*/
public Jackson2HashMapper(boolean flatten) {
this(new ObjectMapper().findAndRegisterModules(), flatten);
this(new ObjectMapper() {
typingMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
@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.activateDefaultTyping(typingMapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING,
As.PROPERTY);
typingMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
// Prevent splitting time types into arrays. E

View File

@@ -30,6 +30,7 @@ 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.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
@@ -71,10 +72,10 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
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 class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
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);
}
}
}

View File

@@ -183,6 +183,15 @@ public abstract class Jackson2HashMapperUnitTests extends AbstractHashMapperTest
assertBackAndForwardMapping(source);
}
@Test // GH-1566
void mapFinalClass() {
MeFinal source = new MeFinal();
source.value = "id-1";
assertBackAndForwardMapping(source);
}
@Data
public static class WithList {
List<String> strings;
@@ -206,4 +215,9 @@ public abstract class Jackson2HashMapperUnitTests extends AbstractHashMapperTest
private LocalDate localDate;
private LocalDateTime localDateTime;
}
@Data
public static final class MeFinal {
private String value;
}
}

View File

@@ -20,11 +20,12 @@ import static org.mockito.Mockito.*;
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 @@ class GenericJackson2JsonRedisSerializerUnitTests {
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 @@ class GenericJackson2JsonRedisSerializerUnitTests {
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 @@ class GenericJackson2JsonRedisSerializerUnitTests {
}
@Data
static final class FinalObject {
public Long longValue;
SimpleObject simpleObject;
}
static class SimpleObject {
public Long longValue;