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 c3922d5f7..6b40694c0 100644
--- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java
+++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java
@@ -16,11 +16,13 @@
package org.springframework.data.redis.serializer;
import java.io.IOException;
+import java.io.Serial;
import java.util.Collections;
import java.util.function.Supplier;
import org.springframework.cache.support.NullValue;
import org.springframework.core.KotlinDetector;
+import org.springframework.data.redis.util.RedisAssertions;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -46,19 +48,38 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
- * Generic Jackson 2-based {@link RedisSerializer} that maps {@link Object objects} to JSON using dynamic typing.
+ * Generic Jackson 2-based {@link RedisSerializer} that maps {@link Object objects} to and from {@literal JSON}
+ * using dynamic typing.
*
- * JSON reading and writing can be customized by configuring {@link JacksonObjectReader} respective
- * {@link JacksonObjectWriter}.
+ * {@literal JSON} reading and writing can be customized by configuring a {@link JacksonObjectReader}
+ * and {@link JacksonObjectWriter}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Mao Shuai
+ * @author John Blum
+ * @see org.springframework.data.redis.serializer.JacksonObjectReader
+ * @see org.springframework.data.redis.serializer.JacksonObjectWriter
+ * @see com.fasterxml.jackson.databind.ObjectMapper
* @since 1.6
*/
public class GenericJackson2JsonRedisSerializer implements RedisSerializer {
- private final ObjectMapper mapper;
+ /**
+ * Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional
+ * {@code classPropertyTypeName}. This method should be called by code that customizes
+ * {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}.
+ *
+ * @param objectMapper the object mapper to customize.
+ * @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
+ * @since 2.2
+ */
+ public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) {
+
+ // Simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here
+ // since we need the type hint embedded for deserialization using the default typing feature.
+ objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));
+ }
private final JacksonObjectReader reader;
@@ -66,21 +87,27 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer defaultTypingEnabled;
+ private final ObjectMapper mapper;
+
private final TypeResolver typeResolver;
/**
- * Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
+ * Creates {@link GenericJackson2JsonRedisSerializer} initialized with an {@link ObjectMapper} configured for
+ * default typing.
*/
public GenericJackson2JsonRedisSerializer() {
this((String) null);
}
/**
- * Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing using the
- * given {@literal name}. In case of an {@literal empty} or {@literal null} String the default
- * {@link JsonTypeInfo.Id#CLASS} will be used.
+ * Creates {@link GenericJackson2JsonRedisSerializer} initialized with an {@link ObjectMapper} configured for
+ * default typing using the given {@link String name}.
+ *
+ * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS}
+ * will be used.
*
- * @param classPropertyTypeName name of the JSON property holding type information. Can be {@literal null}.
+ * @param classPropertyTypeName {@link String name} of the JSON property holding type information;
+ * can be {@literal null}.
* @see ObjectMapper#activateDefaultTypingAsProperty(PolymorphicTypeValidator, DefaultTyping, String)
* @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As)
*/
@@ -89,13 +116,17 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer
+ * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS}
+ * will be used.
*
- * @param classPropertyTypeName name of the JSON property holding type information. Can be {@literal null}.
- * @param reader the {@link JacksonObjectReader} function to read objects using {@link ObjectMapper}.
- * @param writer the {@link JacksonObjectWriter} function to write objects using {@link ObjectMapper}.
+ * @param classPropertyTypeName {@link String name} of the JSON property holding type information;
+ * can be {@literal null}.
+ * @param reader {@link JacksonObjectReader} function to read objects using {@link ObjectMapper}.
+ * @param writer {@link JacksonObjectWriter} function to write objects using {@link ObjectMapper}.
* @see ObjectMapper#activateDefaultTypingAsProperty(PolymorphicTypeValidator, DefaultTyping, String)
* @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As)
* @since 3.0
@@ -105,19 +136,17 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer mapper.getSerializationConfig()
+ .getDefaultTyper(null) != null);
- this.defaultTypingEnabled = Lazy.of(() -> mapper.getSerializationConfig().getDefaultTyper(null) != null);
-
- Supplier typeHintPropertyNameSupplier;
-
- if (typeHintPropertyName == null) {
-
- typeHintPropertyNameSupplier = Lazy.of(() -> {
- if (defaultTypingEnabled.get()) {
- return null;
- }
-
- return mapper.getDeserializationConfig().getDefaultTyper(null)
- .buildTypeDeserializer(mapper.getDeserializationConfig(),
- mapper.getTypeFactory().constructType(Object.class), Collections.emptyList())
- .getPropertyName();
-
- }).or("@class");
- } else {
- typeHintPropertyNameSupplier = () -> typeHintPropertyName;
- }
-
- this.typeResolver = new TypeResolver(Lazy.of(mapper::getTypeFactory), typeHintPropertyNameSupplier);
+ this.typeResolver = new TypeResolver(Lazy.of(mapper::getTypeFactory),
+ newTypeHintPropertyNameSupplier(mapper, typeHintPropertyName, this.defaultTypingEnabled));
}
- /**
- * Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional
- * {@code classPropertyTypeName}. This method should be called by code that customizes
- * {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}.
- *
- * @param objectMapper the object mapper to customize.
- * @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
- * @since 2.2
- */
- public static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) {
+ private Supplier newTypeHintPropertyNameSupplier(ObjectMapper mapper, @Nullable String typeHintPropertyName,
+ Lazy defaultTypingEnabled) {
- // simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need
- // the type hint embedded for deserialization using the default typing feature.
- objectMapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));
+ return typeHintPropertyName != null ? () -> typeHintPropertyName
+ : Lazy.of(() -> defaultTypingEnabled.get() ? null
+ : mapper.getDeserializationConfig().getDefaultTyper(null)
+ .buildTypeDeserializer(mapper.getDeserializationConfig(),
+ mapper.getTypeFactory().constructType(Object.class), Collections.emptyList())
+ .getPropertyName())
+ .or("@class");
}
@Override
@@ -206,8 +211,9 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer T deserialize(@Nullable byte[] source, Class type) throws SerializationException {
- Assert.notNull(type,
- "Deserialization type must not be null Please provide Object.class to make use of Jackson2 default typing.");
+ Assert.notNull(type, "Deserialization type must not be null;"
+ + " Please provide Object.class to make use of Jackson2 default typing.");
if (SerializationUtils.isEmpty(source)) {
return null;
@@ -292,7 +305,9 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer {
+ @Serial
private static final long serialVersionUID = 1999052150548658808L;
+
private final String classIdentifier;
/**
@@ -305,17 +320,19 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer