Spring @RequestBody and Enum value
Asked Answered
B

4

34

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?

Bestialize answered 10/11, 2015 at 18:55 Comment(0)
B
46

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.

Bestialize answered 10/11, 2015 at 21:18 Comment(1)
The JsonCreator annotation (step 2) solved the problem that I was working on. Nice answer.Rakia
F
7

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.

Firecure answered 19/3, 2016 at 22:32 Comment(0)
P
2

I tried @reos ' solution but it kept working only with enum values.

All I needed to do was add the "mode" property of the @JsonCreator annotation to make it work.

public enum RelicTypeEnum {
    UNA("una"),
    MUI("mui");

    private final String text;

    public String getText() {
        return this.text;
    }

    RelicTypeEnum(String text) {
        this.text = text;
    }

    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static RelicTypeEnum fromText(String text) {
        for (RelicTypeEnum r : RelicTypeEnum.values()) {
            if (r.getText().equals(text)) {
                return r;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public String toString() {
        return this.text;
    }
}
Packard answered 6/7, 2022 at 21:16 Comment(0)
C
1

You need to use a custom MessageConverter that calls your custom fromText() method. There's an article here that outlines how to do it.

You extend AbstractHttpMessageConverter<Reos> and implement the required methods, and then you register it.

Carnotite answered 10/11, 2015 at 19:26 Comment(1)
I think this works only if my Review object only contains, the Reos Value. But in my case the object Review has many properties. So I have to map every property to get the Review Object. Another problem is that I have another objects that use Reos enum, so I have to write a converter for each Object. I want a way to write somthing like a PropertyEditor, where I just write a method for the Reos value.Bestialize

© 2022 - 2024 — McMap. All rights reserved.