5

Initial Question

Is it possible to have multiple @JsonCreator methods, and for jackson to detect which one it should use depending on the method definiton?

@JsonCreator
public static StateOfComm factory(String id) {
    return StateOfComm.valueOf(id);
}

@JsonCreator
public static StateOfComm factory(CustomType value) {
    return StateOfComm.valueOf(value.getId());  
}

Update

The JSON that fails (because id=null), is the following:

{"comment":null, "processes":[{"stateOfComm":{"id":"CA"}}]}

The following works:

 {"comment":null, "processes":[{"stateOfComm":"CA"}]}
Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
Menelaos
  • 23,508
  • 18
  • 90
  • 155

3 Answers3

4

I solved this problem by getting rid of the @JsonCreator annotations, and using a custom StdDeserializer that did the trick.

Here is an example:

@JsonIgnoreProperties(
        ignoreUnknown = true
    )
    @JsonDeserialize(
        using = IdTextEntry.Deserializer.class
    )
    @Data
    public class IdTextEntry implements IdAndText {
        String id;
        String text;

        public IdTextEntry(Enum<?> val) {
            if (val != null) {
                this.id = val.name();
                this.text = val.toString();
            }

        }        

        public static class Deserializer extends StdDeserializer<IdTextEntry> {
            public Deserializer() {
                this((Class)null);
            }

            Deserializer(Class<?> vc) {
                super(vc);
            }

            public IdTextEntry deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
                JsonNode node = (JsonNode)jp.getCodec().readTree(jp);
                String id;
                if (node.has("id") && node.has("text")) {
                    id = node.get("id").asText();
                    String text = node.get("text").asText();
                    return new IdTextEntry(id, text);
                } else if (node.has("id")) {
                    id = node.get("id").asText();
                    return new IdTextEntry(id, id);
                } else {
                    id = node.asText();
                    return new IdTextEntry(id, id);
                }
            }
        }
    }
Menelaos
  • 23,508
  • 18
  • 90
  • 155
3

I was able to parse both JSON examples in your question by:

  1. using jackson-modules-java8 version 2.9.1 dependency
  2. invoking the java 8 compiler with -parameters argument
  3. introducing all argument constructors for all classes involved
  4. avoiding @JsonProperty on static creation methods and constructors
  5. defining a class:

    class CustomType {
        private final String id;
    }
    

My understanding is that Jackson couldn't discern between multiple creators in older version. E.g. see answer here and github issue here. It seems that the option to have parameter names in compiled code in java 8 helps in this case.

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
-1

You can have multiple @JsonCreator methods but it requires to use @JsonProperty for specifying which property you are initializing.

@JsonCreator
public static StateOfComm factory(@JsonProperty("id") String id) {
    return StateOfComm.valueOf(id);
}

@JsonCreator
public static StateOfComm factory(@JsonProperty("value") CustomType value) {
    return StateOfComm.valueOf(value.getId());  
}
Yogi
  • 1,805
  • 13
  • 24
  • 3
    I did a test and it doesn't work 100%. Adding an example of the JSON. I think @JsonProperty is to specify the order of the properties but does not help to select the specific factory `@JsonCreator` method. – Menelaos Oct 18 '17 at 08:05
  • 1
    @MenelaosBakopoulos Same for me, it was completely ignored. Finally I ended up with only one JsonCreator and checking the value type inside of the method. – Chanandler Bong Jul 05 '19 at 09:06