I Have this enum
public enum Reos { VALUE1("A"),VALUE2("B"); private String text; Reos(String text){this.text = text;} public String getText(){return this.text;} public static Reos fromText(String text){ for(Reos r : Reos.values()){ if(r.getText().equals(text)){ return r; } } throw new IllegalArgumentException(); } }
And a class called Review, this class contains a property of the type enum Reos.
public class Review implements Serializable{ private Integer id; private Reos reos; public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public Reos getReos() {return reos;} public void setReos(Reos reos) { this.reos = reos; } }
Finally I have a controller that receives an object review with the @RequestBody.
@RestController public class ReviewController { @RequestMapping(method = RequestMethod.POST, value = "/reviews") @ResponseStatus(HttpStatus.CREATED) public void saveReview(@RequestBody Review review) { reviewRepository.save(review); } }
If I invoke the controller with
{"reos":"VALUE1"}
There is not problem, but when I invoke with
{"reos":"A"}
I get this error
Could not read document: Can not construct instance of com.microservices.Reos from String value 'A': value not one of declared Enum instance names: [VALUE1, VALUE2] at [Source: java.io.PushbackInputStream@23ce847a; line: 1, column: 40] (through reference chain: com.microservices.Review["reos"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of com.microservices.Reos from String value 'A': value not one of declared Enum instance names: [VALUE1, VALUE2] at [Source: java.io.PushbackInputStream@23ce847a; line: 1, column: 40] (through reference chain: com.microservices.Review["reos"])"
I undertand the problem, but I wanted to know a way to tell Spring that for every object with Reos enum use Reos.fromText() instead of Reos.valueof().
Is this possible?
We can then use this enum as a RequestParameter in a Spring controller: @GetMapping("/mode2str") public String getStringToMode(@RequestParam("mode") Modes mode) { // ... } Or we can use it as a PathVariable: @GetMapping("/findbymode/{mode}") public String findByEnum(@PathVariable("mode") Modes mode) { // ... }
You only need to make sure they can be converted correctly from or to the format needed for data transfer, and they are immutable, i.e. only a constructor and getters, no setters. So I would use the enum in both the DTO as in the Entity.
I've found what I need. http://chrisjordan.ca/post/50865405944/custom-json-serialization-for-enums-using-jackson.
It was 2 steps.
@Override public String toString() { return text; }
@JsonCreator public static Reos fromText(String text)
And that's all.
I hope this could help others facing the same problem.
I personally prefer writing my own deserializer class using JsonDeserializer
provided by jackson
. You just need to write a deserializer class for your enum. In this example:
class ReosDeserializer extends JsonDeserializer<Reos> { @Override public Reos deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { ObjectCodec oc = jsonParser.getCodec(); JsonNode node = oc.readTree(jsonParser); if (node == null) { return null; } String text = node.textValue(); // gives "A" from the request if (text == null) { return null; } return Reos.fromText(text); } }
Then, we should mark the above class as a deserializer class of Reos as follows:
@JsonDeserialize(using = ReosDeserializer.class) public enum Reos { // your enum codes here }
That's all. We're all set.
In case if you need the serializer for the enum
. You can do that in the similar way by creating a serializer class extending JsonSerializer
and using the annotation @JsonSerialize
.
I hope this helps.
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