Support deserialization in GenericJackson2JsonRedisSerializer when using custom JsonFactory.

Closes: #2981
Original Pull Request: #2999
This commit is contained in:
Mark Paluch
2024-09-16 14:35:10 +02:00
committed by Christoph Strobl
parent f2752d1b02
commit 8c74d78872
3 changed files with 81 additions and 6 deletions

View File

@@ -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<Objec
Lazy<String> lazyTypeHintPropertyName = typeHintPropertyName != null ? Lazy.of(typeHintPropertyName)
: newLazyTypeHintPropertyName(mapper, defaultTypingEnabled);
return new TypeResolver(lazyTypeFactory, lazyTypeHintPropertyName);
return new TypeResolver(mapper, lazyTypeFactory, lazyTypeHintPropertyName);
}
private static Lazy<String> newLazyTypeHintPropertyName(ObjectMapper mapper, Lazy<Boolean> defaultTypingEnabled) {
@@ -340,14 +346,13 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
*/
static class TypeResolver {
// need a separate instance to bypass class hint checks
private final ObjectMapper mapper = new ObjectMapper();
private final ObjectMapper mapper;
private final Supplier<TypeFactory> typeFactory;
private final Supplier<String> hintName;
TypeResolver(Supplier<TypeFactory> typeFactory, Supplier<String> hintName) {
TypeResolver(ObjectMapper mapper, Supplier<TypeFactory> typeFactory, Supplier<String> hintName) {
this.mapper = mapper;
this.typeFactory = typeFactory;
this.hintName = hintName;
}
@@ -358,7 +363,7 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
protected JavaType resolveType(byte[] source, Class<?> 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<Objec
return constructType(type);
}
/**
* Lenient variant of ObjectMapper._readTreeAndClose using a strict {@link JsonNodeDeserializer}.
*
* @param source
* @return
* @throws IOException
*/
private JsonNode readTree(byte[] source) throws IOException {
JsonDeserializer<? extends JsonNode> 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);
}
}
}
}
/**