Skip to content

Serialized result of java Class & Record is different with RedisTemplate #3512

Closed
@doljae

Description

@doljae

Describe the bug
Serialized result of java Class & Record is different with RedisTemplate.

Version information
2.13.3

To Reproduce

====== RedisConfiguration ======
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnClass(RedisOperations.class)
public class RedisConfiguration {

    @Bean
    public RedisTemplate<String, ?> jacksonRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        final RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();

        final RedisTemplate<String, ?> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        final GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = genericJackson2JsonRedisSerializer();

        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        return template;
    }

    @Bean
    public GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer() {
        final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new ParameterNamesModule(JsonCreator.Mode.DEFAULT))
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule())
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),
                                     DefaultTyping.NON_FINAL,
                                     As.PROPERTY);

        return new GenericJackson2JsonRedisSerializer(mapper);
    }
}

====== RequestDto ======
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RequestDto {

    private Long id;
    private String name;
    private OffsetDateTime createdAt;
}

====== RequestRecord ======
public record RequestRecord(Long id,
                            String name,
                            OffsetDateTime createdAt) {
}


====== Test Code ======
@SpringBootTest
class RedisRecordTest {

    @Autowired
    private RedisTemplate<String, RequestRecord> redisRecordTemplate;
    @Autowired
    private RedisTemplate<String, RequestDto> redisDtoTemplate;
    @Autowired
    private RedisTemplate<String, String> redisStringTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisObjectTemplate;
    @Autowired
    private ObjectMapper mapper;

    @DisplayName("record -> String -> set() -> get() -> String -> record -> O")
    @Test
    void test() throws JsonProcessingException {
        final RequestRecord record = new RequestRecord(1L, "doljae", OffsetDateTime.now());
        final String serialized = mapper.writeValueAsString(record);
        System.out.println(serialized);
        redisStringTemplate.opsForValue().set("key", serialized);

        final String fromRedis = redisStringTemplate.opsForValue().get("key");
        System.out.println(fromRedis);
        final RequestRecord deserialized = mapper.readValue(fromRedis, RequestRecord.class);
        System.out.println(deserialized);
    }

    @DisplayName("Class -> set() -> get() -> Class -> O")
    @Test
    void test2() {
        final RequestDto dto = new RequestDto(1L, "doljae", OffsetDateTime.now());
        redisDtoTemplate.opsForValue().set("key", dto);
        /*
         redis-cli
         get key
         -> "{\"@class\":\"com.example.redis.RequestDto\",\"id\":1,\"name\":\"doljae\",\"createdA\":\"2022-06-08T21:39:01.166705+09:00\"}"
         */

        final RequestDto fromRedis = redisDtoTemplate.opsForValue().get("key");
        System.out.println(fromRedis);
    }

    @DisplayName("record -> set() -> get() -> record -> X")
    @Test
    void test3() {
        final RequestRecord record = new RequestRecord(1L, "seokjae", OffsetDateTime.now());
        redisRecordTemplate.opsForValue().set("key", record);
        /*
         redis-cli
         get key
         -> "{\"id\":1,\"name\":\"doljae\",\"createdAt\":\"2022-06-08T21:40:36.057138+09:00\"}"
         */

        // Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'
        // at [Source: (byte[])"{"id":1,"name":"doljae","createdAt":"2022-06-08T21:40:36.057138+09:00"}"; line: 1, column: 72]
        final RequestRecord deserialized = redisRecordTemplate.opsForValue().get("key");
    }
}

Expected behavior
Both class and record objects should be stored and deserialized normally in RedisTemplate.

Additional context

  mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),
                                     DefaultTyping.EVERYTHING,
                                     As.PROPERTY);
  • but this way exposes every meta data of object's fields...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions