-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FactoryBasedEnumDeserializer
unable to deserialize enum object with Polymorphic Type Id ("As.WRAPPER_ARRAY") - fails on START_ARRAY token
#3569
Comments
Thank you for reporting the issue, as well as including a reproduction.
Wrapping in an array is likely for |
@cowtowncoder Thanks for your prompt response. Here's the Frequency Class:
Here's how ObjectMapper is used inside GenericJackson2JsonRedisSerializer [FYI - the below snippet is from Spring-data-redis 2.7.2 version]
Please do let me know if you need more info. In my perspective, wrapping enum values during serialisation is fine - having 1st element as class and 2nd element as value. This can be deserialised properly by EnumDeserializer but not by FactoryBasedEnumDeserializer. There is an exception if such kind of enum value is deserialised by FactoryBasedEnumDeserializer. |
Ok, this is quite close, but I think the last part is just to tie it all together in full reproduction. One problem I do see is use of But having said that, I also think code also should rather use So the wrapping into 2-element array is done by Polymorphic type handler, not as part of Enum (value) serializer or deserializer -- type handlers are What the test should be able to show, I think, is where does the wrapping/unwrapping fail. But I am also bit suspicious of custom |
FactoryBasedEnumDeserializer
unable to deserialize enum object with Polymorphic Type Id ("As.WRAPPER_ARRAY") - fails on START_ARRAY token
One last thing: by "full" reproduction all I mean is that from the original examples write + read things would go through without references to any external code outside of |
@cowtowncoder As you quoted: Actual value deserializer like FactoryBasedEnumDeserializer does delegate calls to TypeDeserializer. I agree with your thought process. Having WRAPPER_ARRAY makes more sense. However, I am bit surprised at the behaviour where EnumDeserializer works fine to deserialize enum values but FactoryBasedEnumDeserializer fails. |
@cowtowncoder Do you need any details from my side? |
I think this makes sense. I do need a unit test however, ideally stripped to minimal size. The main open question for me is whether this would be an issue with |
@cowtowncoder In response to your latest comment: In my perspective, this would be an issue with FactoryBasedEnumDeserializer with standard default typing configuration (bigger problem). I have shared the unit test above. Would you require a trimmed/minimal version of the same? I think the one I mention above is the most basic one and doesn't have any extra details. If you think I can get rid of anything, let me know. I am happy to provide any details/code which you may require to get things going. Happy to contribute ! |
@nitinsatapara yes, reproduction needs to have no external dependencies (no Spring), that's the main thing. I am bit concerned by use of custom setup of default typing, including |
@cowtowncoder I have an example for you. This is impacting me as well, even with DefaultType.NON_FINAL and JsonTypeInfo.As.WRAPPER_ARRAY
with Foo and Bar defined as
I get an error deserializing the string back. Note that the input shows as an empty string. com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of As far a I can tell, the issue is with FactoryBasedEnumDeserializer#deserialize:
Is the wrong deserializer being used somehow? |
First of, thank you @rmekhail-tebra for the reproduction! This is super helpful. As to the issue, it is difficult to say without digging deeper: this is pretty complex case that combines 2 things that do not play nicely together -- polymorphic deserialization and generic Java types. |
thanks @rmekhail-tebra for sharing the test case. I faced exactly the same error. |
I think this issue was resolved in later versions, judging by the implementation changes in 2.15 per GitHub. For now I get around this issue by converting the enum to a string since upgrading Jackson isn't an option ATM |
@rmekhail-tebra If there is any chance you could see if your test case did pass on 2.15.2 (or, 2.15.3 released yesterday), that'd be very useful information to add. |
Again, thank you for super helpful reproduction! I tried testing in latest |
Filed an issue for slowly fading out |
Here is working example in 2.15. Works as expected, as mentioned above. public class TestDefaultForEnums
{
static class Foo3569<T> {
public T item;
}
enum Bar3569 {
ENABLED, DISABLED, HIDDEN;
@JsonCreator
public static Bar3569 fromValue(String value) {
String upperVal = value.toUpperCase();
for (Bar3569 enumValue : Bar3569.values()) {
if (enumValue.name().equals(upperVal)) {
return enumValue;
}
}
throw new IllegalArgumentException("Bad input [" + value + "]");
}
}
public void testEnumAsWrapperArrayWithCreator() throws JsonProcessingException
{
ObjectMapper objectMapper = JsonMapper.builder()
.activateDefaultTyping(
new DefaultBaseTypeLimitingValidator(),
ObjectMapper.DefaultTyping.EVERYTHING,
JsonTypeInfo.As.WRAPPER_ARRAY)
.build();
Foo3569<Bar3569> expected = new Foo3569<>();
expected.item = Bar3569.ENABLED;
// First, serialize
String serialized = objectMapper.writeValueAsString(expected);
// Then, deserialize with TypeReference
assertNotNull(objectMapper.readValue(serialized, new TypeReference<Foo3569<Bar3569>>() {}));
// And, also try as described in [databind#3569]
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(Foo3569.class, new Class[]{Bar3569.class});
assertNotNull(objectMapper.readValue(serialized, javaType));
}
} |
Considered fixed or work-around-able, via #4159. |
Describe the bug
FactoryBasedEnumDeserializer is unable to deserialize enum value which is wrapped in Array.
Version information
This is for Jackson 2.13.1 - It worked fine for release 2.10.1
To Reproduce
If you have a way to reproduce this with:
Value is serialized as : ["Frequency","DAILY"]
This results in exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of
Frequency, problem: Unexpected value '' at [Source: (byte[])"["Frequency","DAILY"]"; line: 1, column: 21] at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:47) at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:2047) at com.fasterxml.jackson.databind.DeserializationContext.handleInstantiationProblem(DeserializationContext.java:1400) at com.fasterxml.jackson.databind.deser.std.FactoryBasedEnumDeserializer.deserialize(FactoryBasedEnumDeserializer.java:182) at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3690) at EnumDeserializeTest.main(EnumDeserializeTest.java:26)
Expected behavior
Deserialization should work fine with FactoryBasedEnumDeserializer but fails when it encounters START_ARRAY token. EnumDeserializer works just fine and it is able to parse the array tokens and retrieves the enum value. Similarly, FactoryBasedEnumDeserializer should also work.
Additional context
This issue is faced when using GenericJackson2JsonRedisSerializer. A change was made to this serialiser in Spring-data-redis 2.7.2 which uses JsonTypeInfo.Id.CLASS annotation as default for all types. Prior to this release, enum types were serialised as simple/plain values but with this change they are wrapped in an array where 1st element is denoted for class and 2nd element holds the enum value.
The text was updated successfully, but these errors were encountered: