spring-projects / spring-data-redis

Provides support to increase developer productivity in Java when using Redis, a key-value store. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-redis/
Apache License 2.0
1.75k stars 1.15k forks source link

Jackson2HashMapper fails to deserialize enum property when flatten=true #2979

Closed jpyoder closed 2 weeks ago

jpyoder commented 2 weeks ago

Prior to version 3.0.0 of spring-data-redis, Jackson2HashMapper serialized and deserialized enum properties (with flatten=true) without a problem. From 3.0.0 and up, deserialization from the hash fails if flatten=true.

A simple test proves this out - see demo.zip for a full example. Basically, for this code, the call to fromHash will fail with an exception.

class Wrapper {
    //Getters and setters omitted for brevity
    private EnumVal val;
}

enum EnumVal {
    FOO;
}
...

Wrapper wrapper = new Wrapper();
wrapper.setVal(EnumVal.FOO);
final Jackson2HashMapper flatteningMapper = new Jackson2HashMapper(true);
var result = flatteningMapper.toHash(wrapper);
var flattenedResult = flatteningMapper.fromHash(result);

The stack trace is:


Unexpected token (null), expected START_ARRAY: need Array value to contain `As.WRAPPER_ARRAY` type information for class com.example.demo.DemoApplicationTests$EnumVal
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 23] (through reference chain: com.example.demo.DemoApplicationTests$Wrapper["val"])
org.springframework.data.mapping.MappingException: Unexpected token (null), expected START_ARRAY: need Array value to contain `As.WRAPPER_ARRAY` type information for class com.example.demo.DemoApplicationTests$EnumVal
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 23] (through reference chain: com.example.demo.DemoApplicationTests$Wrapper["val"])
    at org.springframework.data.redis.hash.Jackson2HashMapper.fromHash(Jackson2HashMapper.java:268)
    at com.example.demo.DemoApplicationTests.testDeserialization(DemoApplicationTests.java:49)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (null), expected START_ARRAY: need Array value to contain `As.WRAPPER_ARRAY` type information for class com.example.demo.DemoApplicationTests$EnumVal
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 23] (through reference chain: com.example.demo.DemoApplicationTests$Wrapper["val"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1913)
    at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1699)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:141)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:96)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromScalar(AsArrayTypeDeserializer.java:66)
    at com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer.deserializeWithType(StdScalarDeserializer.java:66)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:138)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:215)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:170)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:136)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:240)
    at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializerNR.deserializeWithType(UntypedObjectDeserializerNR.java:112)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
    at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2125)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1603)
    at org.springframework.data.redis.hash.Jackson2HashMapper.fromHash(Jackson2HashMapper.java:260)
    ... 4 more
christophstrobl commented 2 weeks ago

Thank you @jpyoder - we'll have a look.