Deserializing enum Shape.OBJECT using Jackson fails

enums, jackson, java, json, serialization

Solution

Whilst I still agree with @renatoaraujoc's answer, I've found a (somehow hacky) solution to this problem.

Just create a `@JsonCreator` annotated method which receives a `Map<String, Object>`. Jackson deserializes the compound object into a Map as passes it to the method:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum TesterEnum {
    FirstValue(1, "One"),
    SecondValue(2, "Two");

    private int id;
    private String dsc;

    TesterEnum(int id, String dsc) {
        this.id = id;
        this.dsc = dsc;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDsc(){
        return dsc;
    }
    public void setDsc(String dsc){
        this.dsc = dsc;
    }

    @JsonCreator
    public static TesterEnum fromObject(final Map<String, Object> obj) {
        if (obj != null && obj.containsKey("id")) {
            Integer id = null;
            if (obj.get("id") instanceof Integer) {
                id = (Integer)obj.get("id");
            } else {
                id = Integer.parseInt((String)obj.get("id"));
            }
            return fromId(id);
        }
        return null;
    }
    public static TesterEnum fromId(final Integer id) {
        if (id != null) {
            for (TesterEnum e : TesterEnum.values()) {
                if (id.equals(e.getId())) return e;
            }
        }
        return null;
    }
}

P.S.: You don't really need the `fromId` method, it can be all put into `fromObject`, but I use the first on other parts of the system and like to have this separation.

P.S.²: Jackson normally decode the `id` field into an `Integer` instance, so it enters the `if (obj.get("id") instanceof Integer)` on `fromObject`, but I've seen it being cast to `String` sometimes, that's why I'm checking.

Problem

I have the following enum declaration: ``` @Document @JsonFormat(shape= JsonFormat.Shape.OBJECT) @JsonAutoDetect() public enum Compass { north("Upper Center"), south("Lower Center"), east("Left Center"), west("Right Center"), ne("Upper Right"), nw("Upper Left"), se("Lower Right"), sw("Lower Left"), ; @JsonProperty private String presentableName; @JsonProperty private String name; private Compass() {} private Compass(String presentableName) { this.presentableName = presentableName; } public String getPresentableName() { return presentableName; } public void setPresentableName(String presentableName) { this.presentableName = presentableName; } public String getName() { return name; } public void setName(String name) { this.name = name; } @JsonCreator public static Compass fromObject(@JsonProperty("name") String name, @JsonProperty("presentableName") String presentableName) { return Compass.sw; } } ``` The input arrives as a json object and most of it is deserialized correctly, but the relevant part is as below, where `placement` is a `Compass`: ``` {"placement":{"name":"se","presentableName":"Lower Right"}} ``` Deserialization doesn't work. I thought a `JsonCreator` would work here, but for some reason I'm getting a org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported which is actually just a symptom of deserialization failure. If I change the creator to: ``` @JsonCreator public static Compass fromObject(@JsonProperty("name") String name) { return Compass.sw; } ``` It gets even more wierd, because now name equals `{` instead of `se` (which looks like a bug in the json object, but it's the same object that got deserialized a second ago so it's probably ok) I'm using `jackson 2.2.3` which is the latest right now.

Original source