jackson-databind: `@JsonValue` with integer for enum does not deserialize correctly

The Javadoc for @JsonValue states that it is the only required annotation to both serialize and deserialize enums as something other than their name or ordinal:

when use for Java enums, one additional feature is that value returned by annotated method is also considered to be the value to deserialize from, not just JSON String to serialize as. This is possible since set of Enum values is constant and it is possible to define mapping, but can not be done in general for POJO types; as such, this is not used for POJO deserialization.

The Javadoc also states that it can be applied to any scalar type:

Usually value will be of a simple scalar type (String or Number), but it can be any serializable type (Collection, Map or Bean).

However, annotating an enum like below will fail to deserialize – Jackson appears to interpret the integer as the ordinal of the enum.

public enum Type {
    A(2),
    B(3);
    private final int value;
    Type(final int value) { this.value = value; }
    @JsonValue public int value() { return this.value; }
}

When attempting to deserialize an enum like the above example, on Jackson 2.9.2, I receive the following stack trace: (slightly anonymized)

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.example.Type` from number 3: index value outside legal index range [0..1]
                                                                                 at [Source: (InputStreamReader); line: 1, column: 60] (through reference chain: java.util.ArrayList[0]->com.example.Pojo["type"])
                                                                                 at com.fasterxml.jackson.databind.DeserializationContext.weirdNumberException(DeserializationContext.java:1563)
                                                                                 at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdNumberValue(DeserializationContext.java:953)
                                                                                 at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:200)
                                                                                 at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:149)

If I add a @JsonCreator to a static method that matches the value to the internal field, the enum can be deserialized correctly. However that is more code I would rather not maintain if possible.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 35
  • Comments: 25 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Hi, I had same issue today, I managed to resolve that using this 2 annotations implemented within my int coded enum:

public enum MyEnum {
    SOME_MEMBER1(10),
    SOME_MEMBER2(15),

    private int code;

    MyEnum(int code) {
       this.code = code;
    }

   public int getCode() {
      return code;
   }

   @JsonValue
   public int toValue() {
        return getCode();
   }

   public static MyEnum forCode(int code) {
       for (MyEnum element : values()) {
          if (element.code == code) {
             return element;
          }
      }
      return null; //or throw exception if you like...
   }

   @JsonCreator
   public static MyEnum forValue(String v) {
       return MyEnum.forCode(Integer.parseInt(v));
   } 
}

Hope that helps someone else too, I did spent quite some time to find the variant that works… Cheers 😉

I’ve managed to fix it by setting @JsonCreator mode to DELEGATING and having @JsonValue on my id field e.g.

SOME_VALUE(1), OTHER_VALUE(2)

@JsonValue private Integer id;

// getter   

@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
static MyEnum fromId(int id){
    return Stream.of(MyEnum.values()).filter(enum -> enum.id == id).findFirst().get();
}

Hi. My issue is exactly the same as the one described originally.

I have also recently experienced this and it was certainly a mystery trying to figure it out. In my case it didn’t fail, it just picked the wrong enum (the corresponding ordinal in the enum list) rather than the enum I expected. Very, very confusing.

Yes, I can see how it’s not a common case. If it’s deemed not worthy of inclusion, it might help to clarify the javadocs on @JsonValue that the auto-magic deserialization does not work for ints and the client should provide @JsonCreator as they would normally.