Repository navigation
Description
When using GenericJacksonJsonRedisSerializer, enum values are always serialized without type metadata, even when DefaultTyping.NON_FINAL_AND_ENUMS is explicitly configured. This causes ClassCastException during deserialization.
Example
public enum Status {
ACTIVE, INACTIVE
}
@Cacheable(cacheNames = "status", cacheManager = "redisCacheManager")
public Status getStatus(String id) {
return Status.ACTIVE;
}Serialized value in Redis:
"ACTIVE"Exception on deserialization:
java.lang.ClassCastException: class java.lang.String cannot be cast to class Status
Configuration Attempt
GenericJacksonJsonRedisSerializer.create(b -> {
b.enableDefaultTyping(ptv);
b.customize(mb ->
mb.activateDefaultTypingAsProperty(
ptv,
DefaultTyping.NON_FINAL_AND_ENUMS,
"@class"
)
);
});Using customize() to override with Jackson's default DefaultTypeResolverBuilder does enable enum type metadata, but it loses Spring's Kotlin-specific handling for final classes:
// This Kotlin support in Spring's TypeResolverBuilder is lost:
if (javaType.isFinal() && !KotlinDetector.isKotlinType(javaType.getRawClass())
&& javaType.getRawClass().getPackageName().startsWith("java")) {
return false;
}This forces users to choose between enum support and Kotlin data class support.
Root Cause
Spring's TypeResolverBuilder unconditionally excludes enum types regardless of the DefaultTyping setting:
if (javaType.isEnumType() || ClassUtils.isPrimitiveOrWrapper(javaType.getRawClass())) {
return false; // Always excluded
}Suggested Fix
Store the DefaultTyping parameter and use it in useForType():
private static class TypeResolverBuilder extends DefaultTypeResolverBuilder {
private final DefaultTyping defaultTyping;
public TypeResolverBuilder(PolymorphicTypeValidator subtypeValidator, DefaultTyping defaultTyping,
JsonTypeInfo.As includeAs, JsonTypeInfo.Id idType, @Nullable String propertyName) {
super(subtypeValidator, defaultTyping, includeAs, idType, propertyName);
this.defaultTyping = defaultTyping;
}
@Override
public boolean useForType(JavaType javaType) {
if (javaType.isJavaLangObject()) {
return true;
}
javaType = resolveArrayOrWrapper(javaType);
// Respect DefaultTyping.NON_FINAL_AND_ENUMS for enum types
if (javaType.isEnumType()) {
return defaultTyping == DefaultTyping.NON_FINAL_AND_ENUMS;
}
if (ClassUtils.isPrimitiveOrWrapper(javaType.getRawClass())) {
return false;
}
if (javaType.isFinal() && !KotlinDetector.isKotlinType(javaType.getRawClass())
&& javaType.getRawClass().getPackageName().startsWith("java")) {
return false;
}
return !TreeNode.class.isAssignableFrom(javaType.getRawClass());
}
// ... resolveArrayOrWrapper() unchanged
}This preserves backward compatibility (enums excluded by default with NON_FINAL) while allowing enum type metadata when explicitly requested via NON_FINAL_AND_ENUMS.
This preserves backward compatibility while allowing enum type metadata when explicitly requested.
Current Workarounds
- Wrap enums in a generic container class
- Copy and modify Spring's
TypeResolverBuilder(not maintainable)
Environment
- Spring Data Redis: 4.0.x
- Jackson: 3.x
Activity
chanho0912 commentedon Feb 10, 2026
@mp911de
Hi, I see this has been assigned already, but I'd be happy to submit a PR if that would be helpful. Please let me know.
mp911de commentedon Feb 10, 2026
Thank you for your issue report. We appreciate community contributions, but we do need them to meet a reasonable baseline for consideration.
Tip
This means:
When issues require significant effort from our side to initially understand your request, we have to deprioritize or even reject them in favor of tickets that are ready for discussion as we have to be mindful of our limited time, and we need to focus it where it has the most impact. If you would like us to spend some time working on this issue, please spend some time making this a substantial and quality ticket description.
When describing your concern, please focus on the aspects that matter. There's no need to duplicate parts of our code in the description or use excessive summarization or bullet lists. Instead, use rather links if you want to point out subtle details that matter in the specific context. Avoid conflicting requests such as asking for guidance and then answering questions yourself, as such descriptions neglect raising a ticket in the first place.
Important
Help us understand:
Concise, thoughtful explanations of your decision-making process are far more valuable than detailed restatements of the code.
I've explored your suggestion and it seems reasonable. I've created a pull request #3309 myself as it makes sense to mirror settings between Jackson 2 and Jackson 3 variants and our configuration code has quite some nuances that even AI-assisted code generation gets wrong and that requires much more time on our side instead of us simply doing the changes.
Just adding
DefaultTypingisn't likely going to achieve what you're looking for because acceptingDefaultTypingrequires us to adhere to theNON_FINALpart ofNON_FINAL_AND_ENUMS, too. It would be a strange behavior to acceptNON_FINAL_AND_ENUMSas typing configuration resulting with type hints being written for final types (such as records).I think this issue requires much more thought and an attempt would be a type hint registry that can be built from
Predicate<JavaType>for more precise control over types that should be equipped with type hints.[-]GenericJacksonJsonRedisSerializer ignores DefaultTyping.NON_FINAL_AND_ENUMS for enum types[/-][+]`RedisCache` with `GenericJacksonJsonRedisSerializer` breaks when using enums[/+]chanho0912 commentedon Feb 11, 2026
@mp911de
Thank you for the quick fix and the feedback on the issue description.
mp911de commentedon Feb 11, 2026
This is not a fix. This is a draft that shows shortcomings of using
DefaultTyping.Add Predicate to control default type mapping.
Add Predicate to control default type mapping.
Add Predicate to control default type mapping.
Add default type mapping config for Jackson serializer.
fix: enable enum type metadata serialization with NON_FINAL_AND_ENUMS