Skip to content

fix: enable enum type metadata serialization with NON_FINAL_AND_ENUMS#3364

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

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

@lukman48 lukman48 commented May 7, 2026

Copy link
Copy Markdown

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

lukman48 commented May 8, 2026

Copy link
Copy Markdown
Author

✅ 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! 🚀

@mp911de mp911de self-assigned this Jun 1, 2026
@mp911de

mp911de commented Jun 3, 2026

Copy link
Copy Markdown
Member

We do have a design draft #3309 that addresses this issue. Serializers can be used in various contexts and so we want to provide an API that allows expressing sufficient control depending on the actual usage, hence closing this pull request as duplicate.

@mp911de mp911de closed this Jun 3, 2026
@mp911de mp911de added status: duplicate A duplicate of another issue and removed status: waiting-for-triage An issue we've not yet triaged labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: duplicate A duplicate of another issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RedisCache with GenericJacksonJsonRedisSerializer breaks when using enums

3 participants