I need to convert two specific fields before mapping to Dto class. Data i get from form:
userName: test
password: 123
active: N
enabled: N
external: N
id: -1
Dto class:
@Getter @Setter
public class UserDto {
protected Integer id;
protected String userName;
protected String password;
protected boolean active = false;
protected boolean enabled = true;
protected String external;
}
Fields active
and enabled
are a boolean type, but from form i get "Y" or "N" string values. Cause of it, i got an error Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'active'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [N]
I've read about the Сonverter<T,V> and PropertyEditors, but I'm not sure if they work for me and how can i apply it only for 2 specific fields.
Controller:
@RequestMapping(value = "/user/save",method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@ModelAttribute UserDto userDto, BindingResult bindingResultM, HttpServletRequest req) {
List<FieldError> errors = bindingResultM.getFieldErrors();
for (FieldError error : errors) {
log.error("bindingResultM: " error.getObjectName() " - " error.getDefaultMessage());
}
try {
userService.saveUserDto(userDto);
return new ResponseEntity<>("OK", HttpStatus.OK);
} catch (Exception e){
throw new ResponseException(e.getMessage(), HttpStatus.METHOD_FAILURE);
}
}
CodePudding user response:
On your DTO you can annotate your boolean fields with :
@JsonDeserialize(
using = BooleanDeserializer.class
)
protected boolean enabled;
And create the deserializer :
public class BooleanDeserializer extends JsonDeserializer<Boolean> {
public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String booleanValue = StringUtils.trimToEmpty(jsonParser.getText().trim());
return BooleanUtils.toBoolean(booleanValue, "Y", "N");
}
}
CodePudding user response:
Because you are saying you need to get data from the form to populate the UserDTO object, which is a process of translate String type to other type.
So, you need to use "org.springframework.core.convert.converter.Converter", "java.beans.PropertyEditor", or "org.springframework.format.Formatter". All of three can do the job of translating String type to other type or other type to String type.
But PropertyEditor is too complicated, so usually we always use Converter or Formatter, and these two can be set in the same method "default void addFormatters(FormatterRegistry registry) {}" of the "WebMvcConfigurer" interface.
But that's for the whole application, you only want the translation happen for "active" and "enabled" fields of UserDTO.
Then you need to use the "org.springframework.web.bind.annotation.InitBinder", which can be defined inside the Controller for specific method parameter. The "userDTO" of the "@InitBinder" means the data binder is only used for the "userDTO" method parameter inside this Controller. (This name is case-sensitive, so if you "userDto" in the method, you need to change it to userDto in the @InitBinder annotation.) Inside this method, you can specify the Formatter only for "active" and "enabled" fields, by using this method:
public void addCustomFormatter(Formatter<?> formatter, String... fields){}
Of course you can specify the Converter for these two fields, but there is no direct method for it in the "WebDataBinder" class. So, it is eaiser to use the Formatter.
@InitBinder("userDTO")
public void forUserDto(WebDataBinder binder) {
binder.addCustomFormatter(new BooleanFormatter(), "active", "enabled");
}
Here is the BooleanFormatter class:
public class BooleanFormatter implements Formatter<Boolean>{
@Override
public String print(Boolean object, Locale locale) {
if (object) {
return "Y";
}
return "N";
}
@Override
public Boolean parse(String text, Locale locale) throws ParseException {
if (text.equals("Y")) {
return true;
}
return false;
}
}