Search code examples
javaspringredisspring-data-redisjsonserializer

Redis caching Map<Integer, String>


I an using redis cache and faced the problem: map with integer key is serialized as String like this:

 "1":"AAAA","2":"BBB","3":"CCC"

This is how my config looks like:

@Bean
    public RedisCacheConfiguration myCacheConfiguration()
    {
        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ZERO)
                .disableCachingNullValues()
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new Jackson2JsonRedisSerializer<>(Map.class)));
    }

  @Bean
    public CacheManager myCacheManager(RedisConnectionFactory redisConnectionFactory)
    {
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(myCacheConfiguration())
                .transactionAware()
                .build();
    }

I tried to pass GenericJackson2JsonRedisSerializer to serializeValuesWith(), but is doesn't work. Is there any way to serialize\deserialize Integer keys of map as number?


Solution

  • The issue is easily solved by adding overriding of method JavaType getJavaType(Class clazz) in Jackson2JsonRedisSerializer. Documentation says:

    /**
         * Returns the Jackson {@link JavaType} for the specific class.
         * <p>
         * Default implementation returns {@link TypeFactory#constructType(java.lang.reflect.Type)}, but this can be
         * overridden in subclasses, to allow for custom generic collection handling. For instance:
         *
         * <pre class="code">
         * protected JavaType getJavaType(Class&lt;?&gt; clazz) {
         *  if (List.class.isAssignableFrom(clazz)) {
         *      return TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, MyBean.class);
         *  } else {
         *      return super.getJavaType(clazz);
         *  }
         * }
         * </pre>
         *
         * @param clazz the class to return the java type for
         * @return the java type
         */
        protected JavaType getJavaType(Class<?> clazz) {
            return TypeFactory.defaultInstance().constructType(clazz);
        }
    

    So, I just overriding this method like this:

    public class CustomSerializer extends Jackson2JsonRedisSerializer
    {
        public JurisdictionsSerializer(Class type)
        {
            super(type);
        }
    
    
        @Override
        protected JavaType getJavaType(Class clazz)
        {
            return TypeFactory.defaultInstance()
                    .constructMapType(Map.class, Integer.class, String.class);
        }
    }
    

    And then add this serializer to redis configuration like this:

    @Bean
        public RedisCacheConfiguration myCacheConfiguration()
        {
            return RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ZERO)
                    .disableCachingNullValues()
                    .serializeValuesWith(RedisSerializationContext.SerializationPair
                            .fromSerializer(new CustomSerializer(Map.class)));
        }