Home > Back-end >  How to write a custom convertor for recieving data annotated with @RequestBody
How to write a custom convertor for recieving data annotated with @RequestBody

Time:09-24

I'm trying to write a custom convertor for receiving data POSTed to a REST application. The object I want to populate already has its own builder that accepts a string JSON so I have to use that instead of the Jackson deserializer Spring would normally use.

I've tried a number of different things but I keep getting the following exception:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class xxx.yyy.zzz.MyType]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.yyy.zzz.MyType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (PushbackInputStream); line: 1, column: 1]
        at         at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:281) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:250) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]

My convertor looks like:

public class MyConverter extends AbstractHttpMessageConverter<MyType> {
    public MyConverter() {
        super(/*MediaType.TEXT_PLAIN*/ MediaType.APPLICATION_JSON);
    }

    @Override
    protected boolean supports(Class<?> type) {
        return MyType.class.isAssignableFrom(type);
    }

    @Override
    protected MyType readInternal(Class<? extends MyType> type, HttpInputMessage inputMessage) throws IOException {
        String str = ..... read data from inputMessage

        return MyType.build(str);
    }

    @Override
    protected void writeInternal(MyType s, HttpOutputMessage outputMessage) {
    }
}

and the controller is:

@RequestMapping(method = RequestMethod.POST)
public void add(@RequestBody MyType data) {
    System.out.println("add:"   data.toString());
}

Even if change the MediaType in the constructor for MyConverter to 'MediaType.ALL' it will still fail. Curiously if I change it to TEXT_PLAIN and POST with Content-Type set to 'text/plain' it works!

CodePudding user response:

Answering my own question. The problem turned out to be the order the HttpMessageConverter objects are processed. The inbuilt converters were being processed, and failing, before my converter had chance.

This isn't the best code, but it works:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendMessageConverters(@Nonnull List<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> temp = new ArrayList<>();
        temp.add(new MyConverter());
        temp.addAll(converters);

        converters.clear();
        converters.addAll(temp);
    }
}

I can't quite believe this is the best answer for what, I think, should be a fairly standard problem. If somebody can suggest a better answer I'll happily accept that instead

  • Related