Introduce custom StdTypeResolverBuilder to support primitive arrays without type hints.
Closes: #2361 Original Pull Request: #2364
This commit is contained in:
committed by
Christoph Strobl
parent
0c51d99e9b
commit
0e23a805ca
@@ -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<Objec
|
||||
// the type hint embedded for deserialization using the default typing feature.
|
||||
registerNullValueSerializer(mapper, classPropertyTypeName);
|
||||
|
||||
StdTypeResolverBuilder typer = new TypeResolverBuilder(DefaultTyping.EVERYTHING,
|
||||
mapper.getPolymorphicTypeValidator());
|
||||
typer = typer.init(JsonTypeInfo.Id.CLASS, null);
|
||||
typer = typer.inclusion(JsonTypeInfo.As.PROPERTY);
|
||||
|
||||
if (StringUtils.hasText(classPropertyTypeName)) {
|
||||
mapper.activateDefaultTypingAsProperty(mapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING,
|
||||
classPropertyTypeName);
|
||||
} else {
|
||||
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING, As.PROPERTY);
|
||||
typer = typer.typeProperty(classPropertyTypeName);
|
||||
}
|
||||
mapper.setDefaultTyping(typer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,8 +191,7 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
|
||||
jgen.writeStartObject();
|
||||
jgen.writeStringField(classIdentifier, NullValue.class.getName());
|
||||
@@ -198,4 +204,52 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
|
||||
serialize(value, gen, serializers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link StdTypeResolverBuilder} that considers typing for non-primitive types. Primitives, their wrappers and
|
||||
* primitive arrays do not require type hints. The default {@code DefaultTyping#EVERYTHING} typing does not satisfy
|
||||
* those requirements.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.7.2
|
||||
*/
|
||||
private static class TypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
|
||||
|
||||
public TypeResolverBuilder(DefaultTyping t, PolymorphicTypeValidator ptv) {
|
||||
super(t, ptv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMapper.DefaultTypeResolverBuilder withDefaultImpl(Class<?> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user