I have a rest controller with this method:
@RequestMapping(value = "", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> add(@Valid @RequestBody MyModel myModel, Errors errors) {
...
return new ResponseEntity<SomeObject>(someObject, HttpStatus.OK);
}
In MyModel has a field isMeetingOrSale that is enum (MeetingSaleFlag):
public enum MeetingSaleFlag {
MEETING("MEETING"),
SALE("SALE");
private final String name;
private MeetingSaleFlag(String s) { name = s; }
public boolean equalsName(String otherName) {
return (otherName == null) ? false : name.equals(otherName);
}
public String toString() { return this.name; }
}
and it can map a json that has a field "isMeetingOrSale" : "MEETING"
but the value in the json can be "isMeetingOrSale" : "" or completely missing, so in that case I want the field to be mapped to null. If I change the filed to be Optional<MeetingSaleFlag>
I got
Could not read JSON: Can not instantiate value of type [simple type, class
java.util.Optional<MeetingSaleFlag>] from String value ('MEETING'); no single-String constructor/factory method\\n at [Source: java.io.PushbackInputStream@32b21158; line: 17, column: 18] (through reference chain: MyModel[\"isMeetingOrSale\"]);
So the question is how can I map Optional enum from json?
Thanks to Sotirios Delimanolis's comment I was able to resolve the issue.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
as a dependency.
@Bean
@Primary
public ObjectMapper jacksonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module());
return mapper;
}
OR do this to register the jdk8 module
/**
* @return Jackson jdk8 module to be registered with every bean of type
* {@link ObjectMapper}
*/
@Bean
public Module jdk8JacksonModule() {
return new Jdk8Module();
}
Another way to customize Jackson is to add beans of type com.fasterxml.jackson.databind.Module to your context. They will be registered with every bean of type ObjectMapper, providing a global mechanism for contributing custom modules when you add new features to your application.
Doing this will only register the additional module and keep the built-in Jackson configuration provided by Spring Boot.
Now when the property is missing from the sent json, it's mapped to null
(This is not that great. I was expecting that it will give me an Optional and I will be able to use .isPresent()).
When it's an empty string ("isMeetingOrSale" : ""), Jackson returns an error:
Could not read JSON: Can not construct instance of MyModel from String value '': value not one of declared Enum instance names: [VAL1, VAL2]
which looks OK to me.
Useful links : Jackson jdk8 module, Spring MVC configure Jackson
This is an example from our codebase:
@NotNull // You probably don't want this
@JsonSerialize(using=CountrySerializer.class)
@JsonDeserialize(using=CountryDeserializer.class)
private CountryCode country;
where CountryCode is a complex enum (see nv-i18n) and these are the classes to (de)serialized from/to JSON:
public class CountrySerializer extends JsonSerializer<CountryCode> {
@Override
public void serialize(CountryCode value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(value.getAlpha3()); // Takes the Alpha3 code
}
public Class<CountryCode> handledType() { return CountryCode.class; }
}
and
public class CountryDeserializer extends JsonDeserializer<CountryCode> {
@Override
public CountryCode deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
// You can add here the check whether the field is empty/null
return CountryCode.getByCode(jp.getText());
}
}
You can easily replicate the same scenario using MeetingSaleFlag instead of CountryCode.
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