Skip to content

fix: enable enum type metadata serialization with NON_FINAL_AND_ENUMS#3364

Open
lukman48 wants to merge 1 commit into
spring-projects:mainfrom
lukman48:fix/3306-enum-type-metadata
Open

fix: enable enum type metadata serialization with NON_FINAL_AND_ENUMS#3364
lukman48 wants to merge 1 commit into
spring-projects:mainfrom
lukman48:fix/3306-enum-type-metadata

Conversation

@lukman48
Copy link
Copy Markdown

@lukman48 lukman48 commented May 7, 2026

Description

Fixes issue #3306: RedisCache with GenericJacksonJsonRedisSerializer breaks when using enums.

Problem

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")
public Status getStatus(String id) {
    return Status.ACTIVE;
}

When configured with:

GenericJacksonJsonRedisSerializer.create(b -> {
    b.customize(mb -> mb.activateDefaultTypingAsProperty(
        ptv,
        DefaultTyping.NON_FINAL_AND_ENUMS,
        "@class"
    ));
});

Expected: Enum cached with type metadata, deserializes correctly to Status enum
Actual: Throws ClassCastException: class java.lang.String cannot be cast to class Status

Root Cause

TypeResolverBuilder.useForType() unconditionally excludes enum types regardless of the DefaultTyping setting:

if (javaType.isEnumType() || ClassUtils.isPrimitiveOrWrapper(javaType.getRawClass())) {
    return false;  // Always excluded
}

This doesn't account for DefaultTyping.NON_FINAL_AND_ENUMS which explicitly requests enum type metadata.

Solution

  1. Store DefaultTyping in TypeResolverBuilder - Add a field to preserve the typing configuration
  2. Update useForType() logic - Check the DefaultTyping setting before excluding enums:
    • Return true for enums when DefaultTyping == NON_FINAL_AND_ENUMS
    • Return false for other settings (maintains backward compatibility)
  3. Preserve existing behavior - Kotlin support and other logic unchanged

Implementation Details

Changes Made

File: GenericJacksonJsonRedisSerializer.java

  1. Add defaultTyping field to TypeResolverBuilder
  2. Initialize in all three constructors
  3. Update useForType() to respect the configuration:
if (javaType.isEnumType()) {
    // Respect DefaultTyping.NON_FINAL_AND_ENUMS for enum types
    return defaultTyping == DefaultTyping.NON_FINAL_AND_ENUMS;
}

Test Coverage

Added three test cases in GenericJacksonJsonRedisSerializerUnitTests:

  1. shouldSerializeEnumWithTypeMetadataWhenUsingNonFinalAndEnumsTyping

    • Verifies enums include type metadata when explicitly requested
    • Confirms correct deserialization to Enum type
  2. shouldDeserializeEnumWithoutTypeMetadataUsingNonFinalTyping

    • Verifies backward compatibility
    • Confirms enums excluded with NON_FINAL
  3. shouldHandleEnumWithUnsafeDefaultTyping_StillConvertsToStringDueToLegacyBehavior

    • Documents original bug (enums deserialize as strings with unsafe typing)
    • Shows our fix enables proper handling when configured correctly

Test Results: All 30 existing tests pass ✅

Backward Compatibility

No breaking changes:

  • Enums still excluded by default (NON_FINAL, OBJECT_AND_NON_FINAL, etc.)
  • Only included when explicitly requested via NON_FINAL_AND_ENUMS
  • No API changes
  • All existing tests pass
  • Kotlin support preserved

Validation

mvn clean test -Dtest=GenericJacksonJsonRedisSerializerUnitTests
# Results: Tests run: 30, Failures: 0, Errors: 0, Skipped: 0
# BUILD SUCCESS

Related

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 7, 2026
Fixes issue spring-projects#3306: RedisCache with GenericJacksonJsonRedisSerializer breaks
when using enums.

Problem
-------
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 because
deserializers receive String values instead of Enum types.

Root Cause
----------
The TypeResolverBuilder.useForType() method unconditionally excludes enum types
regardless of the DefaultTyping setting. It did not account for the case where
users explicitly request enum type metadata via NON_FINAL_AND_ENUMS.

Solution
--------
1. Store the DefaultTyping parameter in TypeResolverBuilder
2. Update useForType() to check the DefaultTyping setting:
   - Return true for enums when DefaultTyping == NON_FINAL_AND_ENUMS
   - Return false for enums with other DefaultTyping options (backward compatible)
3. Preserve Kotlin support and other existing logic

Backward Compatibility
---------------------
✓ Enums still excluded by default (NON_FINAL, OBJECT_AND_NON_FINAL, etc.)
✓ Only included when explicitly requested via NON_FINAL_AND_ENUMS
✓ No API changes
✓ All existing tests pass

Test Coverage
-------------
Added three test cases:
- shouldSerializeEnumWithTypeMetadataWhenUsingNonFinalAndEnumsTyping
- shouldDeserializeEnumWithoutTypeMetadataUsingNonFinalTyping
- shouldHandleEnumWithUnsafeDefaultTyping_StillConvertsToStringDueToLegacyBehavior

All 30 existing tests in GenericJacksonJsonRedisSerializerUnitTests pass.

Signed-off-by: lukman48 <lukman_uki@yahoo.co.id>
@lukman48 lukman48 force-pushed the fix/3306-enum-type-metadata branch from bbccb66 to 0965640 Compare May 8, 2026 02:37
@lukman48
Copy link
Copy Markdown
Author

lukman48 commented May 8, 2026

✅ DCO Signature Updated

The commit has been amended with the required DCO (Developer Certificate of Origin) signature.

Changes Made:

  • Added Signed-off-by: lukman48 <lukman_uki@yahoo.co.id> line
  • Force-pushed updated commit to the branch
  • DCO check: ACTION_REQUIREDSUCCESS

Commit Details:

  • Old Hash: bbccb66
  • New Hash: 0965640
  • Signature: Verified and compliant

PR Status:

  • ✅ DCO: SUCCESS
  • ✅ Pull-Request check: SUCCESS
  • ✅ All tests pass

Ready for maintainer review! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RedisCache with GenericJacksonJsonRedisSerializer breaks when using enums

2 participants