Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @RequestBody and Enum value

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?

like image 759
reos Avatar asked Nov 10 '15 18:11

reos


People also ask

How do you pass enum as parameter in spring boot?

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) { // ... }

Should I use enum in DTO?

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.


2 Answers

I've found what I need. http://chrisjordan.ca/post/50865405944/custom-json-serialization-for-enums-using-jackson.

It was 2 steps.

  1. Override the toString method of the Reos enum
@Override public String toString() {     return text; } 
  1. Annotate with @JsonCreator the fromText method of the Reos enum.
@JsonCreator public static Reos fromText(String text) 

And that's all.

I hope this could help others facing the same problem.

like image 84
reos Avatar answered Sep 21 '22 00:09

reos


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.

like image 34
Manoj Shrestha Avatar answered Sep 24 '22 00:09

Manoj Shrestha