Skip to content
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

Issue handling unknown/unmapped Enum keys #1859

Closed
remya11 opened this issue Dec 13, 2017 · 8 comments
Closed

Issue handling unknown/unmapped Enum keys #1859

remya11 opened this issue Dec 13, 2017 · 8 comments
Milestone

Comments

@remya11
Copy link

remya11 commented Dec 13, 2017

Hi,

Trying to deserialize a JSON string using jackson-databind Objectmapper which has a set of Enum attributes within it, but ends up in InvalidFormatException.

This happens when an unknown attribute, which is not defined in the enum comes in the JSON.

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
    objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
    objectMapper.setSerializationInclusion(Include.NON_NULL);
   objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

But, deserialization (objectMapper.readValue(jsonText, .class);) throws an error. "Test" is the unknown attribute that comes in the JSON String to be deserialized.

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize Map key of type com..* from String "Test": not a valid representation, problem:(com.fasterxml.jackson.databind.exc.InvalidFormatException) Cannot deserialize Map key of type com.... from String "Test": not one of values excepted for Enum class: [ ]

Sample of JSON String would look like this.

String jsonText="{\"cartId\":\"31028\",\"userId\":\"106784\",\"attributes\":{\"count\":\"1\",\"amount\":\"10\",\"email\":\"N\",\"Test\":\"No\",\"phone\":\"N\"}}";

The JSON maps to a Cart class, which holds these attributes as a Map.

public class Cart {
  private String cartId;
  private String userId;
  private Map<Attributes,String> attributes;
}

Attributes map to the Enum, as given below.

public enum Attributes {
  Count(0),
  Amount(1),
  Email(2),
  Phone(3);

  private final int value;
  private Attributes(int value) {
    this.value = value;
  }
  public int getValue() {
    return value;
  }
}

Also tried adding this configuration

objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);

This resulted in the Unknown attribute key to be deserialized as null. (i.e., instead of Test=No, it gets deserialized as null=No). So, when a new attribute - say, 'Test' comes in the Json, it would be good if the mapper can ignore that field and proceed with de-serializing the remaining fields that has a match in the Enum, rather than throwing the exception.

@remya11
Copy link
Author

remya11 commented Dec 28, 2017

Hi,

Could you please let me know when this fix could be available?

Regards,
Remya

@cowtowncoder
Copy link
Member

@remya11 if and when someone has time and interest to work on this.

@cowtowncoder
Copy link
Member

Ok working on this. One quick note: if you declare map as EnumMap, things actually work the way you'd like when DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL is enabled.
When type is basic Map the problem is that null is valid key value.

Handling of "as default" is not working so I will try to fix that, but I will not change the way key handling works for simple Map.

Note that you could also consider custom Map sub-class that would simply change put method to ignore attempts to add entries with null as key.

@cowtowncoder cowtowncoder added this to the 2.9.4 milestone Jan 5, 2018
@cowtowncoder cowtowncoder added 2.9 and removed 3.x labels Jan 5, 2018
@cowtowncoder
Copy link
Member

So: handling READ_UNKNOWN_ENUM_VALUES_AS_NULL not changed as it was working as specified (but handling subtly different for EnumMap). Fixed handling so that EnumKeyDeserializer honors READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE as well.

@isuribb
Copy link

isuribb commented Jan 10, 2018

Tried using the "READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE " with the JsonEnumDefaultValue annotation. It worked for the type Enum as expected. Then we have another issue where we are using a Map<Enum,String> object in our Bean class which didnot get resolved with above. When tried debugging, noticed the following code is going inside the class, EnumResolver since we a . It would be good if we can handle the same default value handling done in EnumDeserializer class , on this class as well so that we can use the default value we have defined. Appreciate a quick response.. Thank you.

StdKeyDeserializer :
@OverRide
public Object _parse(String key, DeserializationContext ctxt) throws IOException
{
if (_factory != null) {
try {
return _factory.call1(key);
} catch (Exception e) {
ClassUtil.unwrapAndThrowAsIAE(e);
}
}
EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
? _getToStringResolver(ctxt) : _byNameResolver;
Enum<?> e = res.findEnum(key);
if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
return ctxt.handleWeirdKey(_keyClass, key, "not one of values excepted for Enum class: %s",
res.getEnumIds());
// fall-through if problems are collected, not immediately thrown
}
return e;
}

@cowtowncoder
Copy link
Member

As per comment on the new issue, note that you do need to use EnumMap as type, not Map.
Latter does not work, and will not work, without custom key handler.

@isuribb
Copy link

isuribb commented Jan 10, 2018

Thanks All... We went with a custom deserilizer and defaulting to a custom value and it resolved our issue. EnumMap also was not an option due to thrift model is not supporting any custom maps except map and tree map.

@bsaptarshi
Copy link

Thanks All... We went with a custom deserilizer and defaulting to a custom value and it resolved our issue. EnumMap also was not an option due to thrift model is not supporting any custom maps except map and tree map.

isuribb,
Can you share the solution you implemented? I am facing a similar issue and would like to get my eye on a likewise solution. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants