Search code examples
javaspringspring-mvcbuilder

How to map @RequestBody to Java Object implementing Builder Pattern


I am using a domain object imported from other service which implements builder pattern and hence have no No-arg Constructor and setters. I want to map JSON coming from @RequestBody to this domain object. I know I can annotate it with @JSONPojoBuilder and some other ones but i don't have control over it. So I'm stuck with writing my own JSON to Object mapper which i don't think is a good idea as it involves validations etc. and is tightly coupled with request body.

Code -

@RequestMapping(value = "someurl", method = RequestMethod.POST)
public ResponseEntity<ObjectA> addObject(@PathVariable String id, @RequestBody @NonNull String body) {
    ObjectA obj = service.addObject(id, mapper.mapJSONToObject(body));
    return new ResponseEntity<>(obj , HttpStatus.CREATED);
}

Instead of writing my own mapper, I heard we can use some message convertor and pass our Builder class as some argument. I did not find such examples, if anybody has done it before or can point me to some site, that'd be helpful.


Solution

  • I've never tried that, but in general Spring MVC supports the abstraction of "Custom Converters" that you can write in order to help spring to convert JSON to complicated objects.

    I assume that you have a MyComplicatedClass without no-ops constructor but with a builder. So you can do the following:

    import org.springframework.core.convert.converter.Converter;
    
    @Component // note the converter is also a spring bean so you can, say, inject object mapper and convert to JsonNode-s for example (see below) 
    public class MyComplicatedClassConverter implements Converter<String, MyComplicatedClass> {
         @Autowired // or use constructor injection
         private ObjectMapper objectMapper;
        /**
         * Override the convert method
         * @param object in a string representation to be converted
         * @return
         */
        @Override
        public MyComplicatedClass convert(String object) {
           JsonNode node=  objectMapper.readTree(/*byte array input stream from object string or something */);
            
            return MyComplicatedClass.builder()
                                      .withSomeProperty(node.get("someProperty").textValue()
                                      .withAnotherProperty(node.get("anotherProperty").intValue()
                                      .build();
        }
    
    }
    

    Now, when your application recognizes this converter it allows using MyComplicatedClass in the controller's methods as if spring knows how to convert it. So the code snippet that you've presented in your example should work.

    All this is done without "touching" the code of MyComplicatedClass.