Add support for deserializing objects with generics
Currently there is no RedisSerializer that can deserialize an object with generics like Set<Person>. This is for example a problem when using redis as cache in spring boot:
@Cacheable("personCache)
public Set<Person> getPersons() {
// expensive code
}
Using for example Jackson2JsonRedisSerializer, we could specify Set as type, but it won't handle the Person type. It deserializes to Set<LinkedHashMap> with the LinkedHashMap having the properties of the person. A cache hit results in a ClassCastException because a LinkedHashMap cannot be cast to a Person (similarly, a Set of enums is deserialized as a Set of Strings).
Classes using a generic (Person in this case) should not be forced to implement Serializable, so Jackson based serialization in itself is nice.
Note: Created this issue to refer to in pull request: pull request on the way
Generics are not retained for values, therefore the cache implementation cannot know anything about the type definition. Spring's Cache API does not support generic type hints either, see https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/main/java/org/springframework/cache/Cache.java#L82.
I think using Jackson should work when using type hints, other than that, there's not much we can do here. I'll leave this one open once your PR arrives.
Hi Mark,
thanks for your fast response. I created a pull request: #2375
One of the tests I added shows a serializer that could make the Set<Person> case in my original post work:
@Test // GH-2374
void testSetOfPersons() {
PersonObjectFactory personFactory = new PersonObjectFactory();
Set<Person> personSet = Set.of(personFactory.instance(), personFactory.instance());
JacksonTypeReference2JsonRedisSerializer<Set<Person>> personSetSerializer =
new JacksonTypeReference2JsonRedisSerializer<>(new TypeReference<>() {});
assertThat(personSetSerializer.deserialize(personSetSerializer.serialize(personSet))).isEqualTo(personSet);
}
In a Spring project that uses Redis as cache for @Cacheable, in a configuration Bean for redis cache, I could do something like:
@Configuration
public class RedisCacheConfig {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
JacksonTypeReference2JsonRedisSerializer<Set<Person>> serializer = new JacksonTypeReference2JsonRedisSerializer<>(new TypeReference<>() {
});
RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
return builder -> builder.withCacheConfiguration("personCache", conf);
}
}
Now JacksonTypeReference2JsonRedisSerializer<Set<Person>> deserializes the bytes of the redis DB to a Set<Person> instead of a Set<LinkedHashMap> and the ClassCastException is gone.
Currently solved it in a project this way and it would be nice to make it a bit easier for others to handle this case, hence this PR.
Closing as per https://github.com/spring-projects/spring-data-redis/pull/2375#issuecomment-1541619462