I'm trying and failing to deserialize an enum with Jackson 2.5.4, and I don't quite see my case out there. My input strings are camel case, and I want to simply map to standard Enum conventions.
@JsonFormat(shape = JsonFormat.Shape.STRING) public enum Status { READY("ready"), NOT_READY("notReady"), NOT_READY_AT_ALL("notReadyAtAll"); private static Map<String, Status> FORMAT_MAP = Stream .of(Status.values()) .collect(toMap(s -> s.formatted, Function.<Status>identity())); private final String formatted; Status(String formatted) { this.formatted = formatted; } @JsonCreator public Status fromString(String string) { Status status = FORMAT_MAP.get(string); if (status == null) { throw new IllegalArgumentException(string + " has no corresponding value"); } return status; } }
I've also tried @JsonValue
on a getter to no avail, which was an option I saw reported elsewhere. They all blow up with:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of ...Status from String value 'ready': value not one of declared Enum instance names: ...
What am I doing wrong?
Deserializing JSON String to Enum Using @JsonValue Annotation. The @JsonValue annotation is one of the annotations which we can use for both serializing and deserializing enums. Enum values are constants, and due to this, @JsonValue annotation can be used for both. First we simply add the getter method to our Distance.
To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.
Because enums are automatically Serializable (see Javadoc API documentation for Enum), there is no need to explicitly add the "implements Serializable" clause following the enum declaration.
We've learned that we can't create a subclass of an existing enum. However, an interface is extensible. Therefore, we can emulate extensible enums by implementing an interface.
EDIT: Starting from Jackson 2.6, you can use @JsonProperty
on each element of the enum to specify its serialization/deserialization value (see here):
public enum Status { @JsonProperty("ready") READY, @JsonProperty("notReady") NOT_READY, @JsonProperty("notReadyAtAll") NOT_READY_AT_ALL; }
(The rest of this answer is still valid for older versions of Jackson)
You should use @JsonCreator
to annotate a static method that receives a String
argument. That's what Jackson calls a factory method:
public enum Status { READY("ready"), NOT_READY("notReady"), NOT_READY_AT_ALL("notReadyAtAll"); private static Map<String, Status> FORMAT_MAP = Stream .of(Status.values()) .collect(Collectors.toMap(s -> s.formatted, Function.identity())); private final String formatted; Status(String formatted) { this.formatted = formatted; } @JsonCreator // This is the factory method and must be static public static Status fromString(String string) { return Optional .ofNullable(FORMAT_MAP.get(string)) .orElseThrow(() -> new IllegalArgumentException(string)); } }
This is the test:
ObjectMapper mapper = new ObjectMapper(); Status s1 = mapper.readValue("\"ready\"", Status.class); Status s2 = mapper.readValue("\"notReadyAtAll\"", Status.class); System.out.println(s1); // READY System.out.println(s2); // NOT_READY_AT_ALL
As the factory method expects a String
, you have to use JSON valid syntax for strings, which is to have the value quoted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With