Issue
I'm trying to get my Formatter for my model running. The model contains an annotation like the following code. I have multiple Formatter i don't get running yet, but can't figure out the issue.
public class Customer {
@Trim
private String firstName;
//some other properties, getter and setter
}
The annotation is correctly set, as far as i know:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface Trim {
boolean squashMultipleSpaces() default true;
}
And the AnnotationFormatterFactory
public class TrimAnnotationFormatterFactory implements AnnotationFormatterFactory<Trim> {
public TrimAnnotationFormatterFactory() {
}
public Set<Class<?>> getFieldTypes() {
return Collections.singleton(String.class);
}
public Printer<String> getPrinter(Trim annotation, Class<?> fieldType) {
return new TrimAnnotationFormatterFactory.TrimFormatter(annotation.squashMultipleSpaces());
}
public Parser<String> getParser(Trim annotation, Class<?> fieldType) {
return new TrimAnnotationFormatterFactory.TrimFormatter(annotation.squashMultipleSpaces());
}
private static class TrimFormatter implements Formatter<String> {
private final boolean squashMultipleSpaces;
TrimFormatter(boolean squashMultipleSpaces) {
this.squashMultipleSpaces = squashMultipleSpaces;
}
public String parse(String text, Locale locale) {
return this.process(text);
}
public String print(String object, Locale locale) {
return this.process(object);
}
private String process(String text) {
if (text == null) {
return null;
} else {
return this.squashMultipleSpaces ? text.trim().replaceAll("\\s+", " ") : text.trim();
}
}
}
}
I added my custom AnnotationFormatterFactory to the FormatterRegistry. When I'm dbuggign it, i can see that it is added to the FormatterRegistry successfully.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new TrimAnnotationFormatterFactory());
}
}
And the controller looks like:
@Controller
public class CustomerController {
@PostMapping(value = "/customer")
@ResponseBody
public Customer saveCustomer(@RequestBody Customer customer) {
return customer;
}
}
if my input looks like this
" Christian Walter"
in the controller the model is still the same. I was expecting
"Christian Walter"
in my model.
Why doesn't my formatter work? Or do i have to use PropertyEditor and if so, how could i use it with annotations?
UPDATE: The formatter is successfully registered, but isn't called. And added the controller.
Thanks for your help.
Solution
From your last update, i understand that you're using json to deserialize your object. In this case you have to (in my opinion) write your own deserializer, as :
public class WhiteSpaceTrimmerDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return node.asText().replaceAll("\\s+", " ").trim();
}
}
And use it like :
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public class Customer {
private String firstName;
public String getFirstName() {
return firstName;
}
@JsonDeserialize(using = WhiteSpaceTrimmerDeserializer.class)
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
So " Christian Walter"
will produce "Christian Walter"
.
There's another ways to configure this deserializer, you can look at : Can Jackson be configured to trim leading/trailing whitespace from all string properties?
But why Formatter
is not working ? The only explanation i have is that @RequestBody/@ResponseBody
are not handled by the WebMvcConfigurer
.
Answered By - bubbles
Answer Checked By - Timothy Miller (JavaFixing Admin)