spring-projects/spring-data-redis

`GenericJackson3JsonRedisSerializer` is throwing exception when deserializing a Collection when defaultTyping is enabled

Closed this issue · 6 comments

Hello!

Spring Boot Version: 4.0.0-M3
Java: 25

I have an issue when trying to deserialize a String with type information. For example

  private static final String JSON_STRING ="""
              ["java.util.ArrayList",[{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName","lastName":"lastName"}]]
          """;

When trying to deserialize the object, an exception is throw when trying to resolve the type. This works with GenericJackson2JsonRedisSerializer.
This is the stacktrace when running a test

[ERROR] com.example.redisdemo.RedisdemoApplicationTests.genericJackson3JsonRedisSerializerTest -- Time elapsed: 0.180 s <<< ERROR!
org.springframework.data.redis.serializer.SerializationException: Could not read JSON:Cannot invoke "tools.jackson.databind.JsonNode.isString()" because "jsonNode" is null
        at org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer.deserialize(GenericJackson3JsonRedisSerializer.java:212)
        at org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer.deserialize(GenericJackson3JsonRedisSerializer.java:182)
        at com.example.redisdemo.RedisdemoApplicationTests.genericJackson3JsonRedisSerializerTest(RedisdemoApplicationTests.java:47)
        at java.base/java.lang.reflect.Method.invoke(Method.java:565)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
Caused by: java.lang.NullPointerException: Cannot invoke "tools.jackson.databind.JsonNode.isString()" because "jsonNode" is null
        at org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer$TypeResolver.resolveType(GenericJackson3JsonRedisSerializer.java:482)
        at org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer.resolveType(GenericJackson3JsonRedisSerializer.java:222)
        at org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer.deserialize(GenericJackson3JsonRedisSerializer.java:210)
        ... 5 more

This is the code being used to test the issue

   private static final String JSON_STRING ="""
              ["java.util.ArrayList",[{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName","lastName":"lastName"}]]
          """;

  private GenericJackson3JsonRedisSerializer createGenericJackson3JsonRedisSerializer() {
    return GenericJackson3JsonRedisSerializer.builder()
        .customize(builder -> builder
            .activateDefaultTyping(
                BasicPolymorphicTypeValidator.builder()
                    .allowIfBaseType(Object.class)
                    .build(),
                DefaultTyping.NON_FINAL,
                As.PROPERTY)
            .findAndAddModules())
        .build();
  }

  @Test
  void genericJackson3JsonRedisSerializerTest() {
    var serializer = createGenericJackson3JsonRedisSerializer();
    List<Person> listOfPerson = (List) serializer.deserialize(JSON_STRING.getBytes());
    assertThat(listOfPerson)
        .isNotEmpty()
        .contains(new Person("firstName", "lastName"));
  }

I'm attaching a small application with a test using both GenericJackson2JsonRedisSerializer and GenericJackson3JsonRedisSerializer

Thanks!

redisdemo.zip

thank you @andrew2184 for reporting and providing a reproducer.

Just adding to what @andrew2184 said it seems that the serialized value does not attach java.util.List class type to START_ARRAY token.

org.opentest4j.AssertionFailedError: expected: "["java.util.List",[{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName","lastName":"lastName"},{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName2","lastName":"lastName2"}]] " but was: "[{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName","lastName":"lastName"},{"@class":"com.example.redisdemo.RedisdemoApplicationTests$Person","firstName":"firstName2","lastName":"lastName2"}]" <Click to see difference>

Let me know if its a misconfiguration. Also I confirm its is occurring in RC-1 version (same as @andrew2184 configured).

Attaching the same demo with the additional test.

redisdemo2.zip

Thank you guys for the awesome work

Hi @yurikilian. I had a look at your test and in your case, it's because you are using List.of() which will return java.util.Collections$UnmodifiableRandomAccessList which is final. Because we are using DefaultTyping.NON_FINAL, it won't add the type. It will work if you use a mutable class like ArrayList. Also, this is probably more on the side of Jackson. Thanks for having a look as well.

jeez good catch @andrew2184 . my bad. Thank you anyway

Just to add that with final configuration the same occurs. Aka, Cannot cache List for exemple.

hi @christophstrobl Sorry, I can see that this has been fixed in RC1. I think we can close this ticket. Also I saw that GenericJackson3JsonRedisSerializer was renamed to just GenericJacksonJsonRedisSerializer.