Spring RestTemplate Mapping JSON response to Map throws MismatchedInputException - java

I'm trying to parse the JSON from Github's commit details API into a HashMap. The Sample Json I've used in my tests is here
The test code is
#SpringJUnitConfig(classes = {GithubApiService.class, RestTemplateConfiguration.class})
#EnableAutoConfiguration
public class GithubApiServiceTest {
#Test
public void testGithubResponseJsonToMapConversion(
#Autowired RestTemplate restTemplate,
#Autowired GithubApiService service,
#Value("classpath:github/commit-payload.json") Resource commitPayloadFile) throws IOException {
final String COMMITS_URL = "https://api.github.com/repos/Codertocat/Hello-World/commits/sha";
//Stub response from Github Server
String responseJson = new ObjectMapper().writeValueAsString(
new String(Files.readAllBytes(Paths.get(commitPayloadFile.getFile().getAbsolutePath()))));
MockRestServiceServer mockGithubServer = MockRestServiceServer.createServer(restTemplate);
mockGithubServer.expect(requestTo(COMMITS_URL))
.andRespond(withSuccess().contentType(APPLICATION_JSON).body(responseJson));
//Call API Service
Map<String, Object> result = service.getCommitDetails(COMMITS_URL);
//Expect return type is hashmap
assertThat(result.get("sha")).isEqualTo("6dcb09b5b57875f334f61aebed695e2e4193db5e");
}
}
The Service code is
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class GithubApiService {
#Autowired
private final RestTemplate restTemplate;
public Map<String, Object> getCommitDetails(String commitsUrl) {
ParameterizedTypeReference<Map<String, Object>> responseType = new ParameterizedTypeReference<Map<String, Object>>() {
};
RequestEntity<Void> request = RequestEntity.get(URI.create(commitsUrl)).accept(APPLICATION_JSON).build();
return restTemplate.exchange(request, responseType).getBody();
}
}
This fails converting the JSON response into a Map with the following error (full log here)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.LinkedHashMap` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
"node_id": "MDY6Q29tbWl0NmRjYjA5YjViNTc4NzVmMzM0ZjYxYWViZWQ2OTVlMmU0MTkzZGI1ZQ==",
"html_url": "https://github.com/octocat/Hello-World/commit/6dcb09b5b57875f334f61aebed695e2e4193db5e",
"comments_url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/comments",
Since I'm using spring-boot-starter-web it auto-wires converters including MappingJackson2HttpMessageConverter but debugger shows that the response is being processed by the ByteArrayHttpMessageConverter despite setting content type to application/json.

My bad, I should have used the #RestClientTest annotation intead of #EnableAutoConfiguration, that fixed the problem.
#EnableAutoConfiguration is a more generic annotation which doesn't achieve all the auto config that #RestClientTest does

Related

Understand and configure Spring JSON marshalling configuration of String responses

For a Spring MVC project (not Spring Boot) I'm configuring the JSON converter to customise JSON responses of all REST endpoints i.e. removing null fields and setting a date format. After introducing SpringDoc to the project I've had to add a StringHttpMessageConverter to prevent the generated OpenAPI JSON from being returned as a string.
Without the StringHttpMessageConverter the OpenAPI JSON looks like this:
"{\"openapi\":\"3.0.1\",\"info\":{\"title\":\"OpenAPI definition\",\"version\":\"v0\"},\"servers\":[{\"url\":\"http://localhost:8080\",\"description\":\"Generated server url\"}],\"paths\":{\"/get\":{\"get\":{\"tags\":[\"controller\"],\"operationId\":\"getSomeMap\",\"responses\":{\"200\":{\"description\":\"default response\",\"content\":{\"*/*\":{\"schema\":{\"$ref\":\"#/components/schemas/ImmutableMultimapStringString\"}}}}}}}},\"components\":{\"schemas\":{\"ImmutableMultimapStringString\":{\"type\":\"object\",\"properties\":{\"empty\":{\"type\":\"boolean\"}}}}}}"
With the StringHttpMessageConverter it looks like this, which is the desired result:
{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"paths":{"/get":{"get":{"tags":["controller"],"operationId":"getSomeMap","responses":{"200":{"description":"default response","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ImmutableMultimapStringString"}}}}}}}},"components":{"schemas":{"ImmutableMultimapStringString":{"type":"object","properties":{"empty":{"type":"boolean"}}}}}}
This does however cause problems with several endpoints that return a string as their response. They should return a valid JSON string: "response-string" but instead they return the string as plain text: response-string, omitting the double quotes, making it invalid JSON.
How can I keep the current configuration intact so the SpringDoc OpenAPI JSON is returned correctly while also having endpoints that have a string response return a valid JSON string?
Configuration used:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
WebContentInterceptor webContentInterceptor = new WebContentInterceptor();
webContentInterceptor.setCacheSeconds(0);
webContentInterceptor.setUseExpiresHeader(true);
webContentInterceptor.setUseCacheControlHeader(true);
webContentInterceptor.setUseCacheControlNoStore(true);
registry.addInterceptor(webContentInterceptor);
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json", MediaType.APPLICATION_JSON);
mediaTypes.put("xml", MediaType.APPLICATION_XML);
configurer.favorPathExtension(false);
configurer.favorParameter(true);
configurer.mediaTypes(mediaTypes);
configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// Note that the order matters here! If the StringHttpMessageConverter is add after the jsonConverter
// the documentation JSON is returned as a giant string instead of a (valid) JSON object
converters.add(new StringHttpMessageConverter());
converters.add(jsonConverter());
}
#Bean
public MappingJackson2HttpMessageConverter jsonConverter() {
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.timeZone(TimeZone.getTimeZone(timeZone));
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(
builder.build()
);
jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
return jsonConverter;
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setClassesToBeBound(KioskProfiel.class, KioskProfielRegel.class, TitlesetTO.class, TitlesetTitel.class);
return jaxb2Marshaller;
}
#Bean
public MarshallingHttpMessageConverter marshallingConverter() {
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_XML);
MarshallingHttpMessageConverter marshallingConverter = new MarshallingHttpMessageConverter(jaxb2Marshaller());
marshallingConverter.setSupportedMediaTypes(supportedMediaTypes);
return marshallingConverter;
}
}
Edit
I've tried overriding the OpenApiResource setting the produces value of the endpoint to TEXT_PLAIN_VALUE and too application/json but the problem still persists. Attempting to change the return type from String to TextNode isn't allowed so that doesn't seem to be an option.
Alternatively I've tried to resolve the problem by registering a Filter to correct the malformed response but that to doesn't work.
Maybe I'm still missing something but I'm out of options. With my current project configuration I can't get SpringDoc to return valid OpenAPI JSON when using a custom MappingJackson2HttpMessageConverter. For now I'll stick to Swagger 2.0 and will look into an alternative library to move to OpenAPI 3.0.
A working solution has finally been found! It consists of two parts. The first is configuring the converters. In short we register the default converts after which the default JSON converter, MappingJackson2HttpMessageConverter is removed and our custom JSON converter is added as the first converter to the list of converters. It is important that the custom JSON converter is in the list of converters before the StringHttpMessageConverter else endpoints that return JSON that have a String as their Java return type return the string without double quotes making it invalid JSON.
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.add(0, jsonConverter());
}
#Bean
public MappingJackson2HttpMessageConverter jsonConverter() {
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.timeZone(TimeZone.getTimeZone(timeZone));
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(
builder.build()
);
jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
return jsonConverter;
}
}
Secondly, this causes the OpenAPI JSON to be one big (escaped) string, as mentioned in the question. To resolve this problem we override the openapiJson method (and endpoint) from the OpenApiWebMvcResource class, which is used by default to return the OpenAPI JSON, to produce text/plain instead of application/json. This way the documentation JSON isn't returned as an (escaped) string anymore.
#RestController
public class OpenApiResource extends OpenApiWebMvcResource {
#Override
#Operation(hidden = true)
#GetMapping(value = Constants.API_DOCS_URL, produces = MediaType.TEXT_PLAIN_VALUE)
public String openapiJson(
HttpServletRequest request,
#Value(Constants.API_DOCS_URL) String apiDocsUrl
)
throws JsonProcessingException {
calculateServerUrl(request, apiDocsUrl);
OpenAPI openAPI = this.getOpenApi();
return Json.mapper().writeValueAsString(openAPI);
}
}
Note that for brevity only the relevant methods are listed in both example classes above.
Another quick workaround is to configure swagger-ui to use the YAML version of the OpenAPI documentation (instead of JSON) by simply adding this property to your application.yaml:
springdoc.swagger-ui.url: /v3/api-docs.yaml

RestTemplate | Reading unknown ENUM values as default value

I'm using org.springframework:spring-web:5.1.9.RELEASE and have a plain (not customized) RestTemplate class object I use to send a POST request by utilizing the EXCHANGE method. I want to allow it desirialize unknown ENUM values from the response to their default value (as set using #JsonEnumDefaultValue) instead of failing the whole operation. I've searched and didn't find any easy way to do it, can you give me some help with that? Thanks!
#Service
#CommonsLog
public class TMServerExternalApiRepositoryImpl implements TMServerExternalApiRepository {
private final RestTemplate restRelay;
private TeleMessageProperties tmUrls;
#Autowired
public TMServerExternalApiRepositoryImpl(RestTemplate restTemplate, TeleMessageProperties tmProperties) {
this.restRelay = restTemplate;
this.tmUrls = tmProperties;
}
#Override
public VnvUsersSearchResult getUsersByPhone(String to, String from) {
log.info(String.format("Trying to get users with telephones to: [%s], from: [%s] ", to, from));
return sendRequest(new VnvUserSearchParams(from, to), VnvUsersSearchResult.class, tmUrls.getGetUserByPhoneUrl()).getBody();
}
//the actual post
private <T, K> ResponseEntity<T> sendRequest(K content, Class<T> returnTypeClass, String url) throws HttpClientErrorException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<K> httpEntity = new HttpEntity<>(content, httpHeaders);
return restRelay.exchange(url, HttpMethod.POST, httpEntity, returnTypeClass);
}
}
This is the exception I am getting now:
org.springframework.web.client.RestClientException: Error while extracting response for type [class voiceAndVideo.services.VnvUsersSearchResult] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]
at [Source: (ByteArrayInputStream); line: 1, column: 261] (through reference chain: voiceAndVideo.services.VnvUsersSearchResult["userFrom"]->voiceAndVideo.Util.VNVUser["devices"]->java.util.ArrayList[2]->voiceAndVideo.Util.VNVDevice["type"])
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:117) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
Classes:
public class VnvUsersSearchResult {
private VNVUser userFrom;
...
}
public class VNVUser {
...
protected List<VNVDevice> devices;
...
}
public class VNVDevice {
public Type type;
String mobile;
public enum Type {
#JsonEnumDefaultValue
UNKNOWN(0),
MOBILE(10),
BUSINESS_PHONE(20),
...
int ID;
Type(int i) {
this.ID = i;
}
}
}
You probably need to configure a custom ObjectMapper to the RestTemplate that you are using. You need to enable this feature on the ObjectMapper. Please make sure you are using fasterxml as the package for all these.
In order to configure one, create a JavaConfig file like this:
#Bean
public RestOperations restOperations() {
RestTemplate rest = new RestTemplate();
rest.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
return rest;
}
#Bean
public MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setObjectMapper(myObjectMapper());
return converter;
}
#Bean
public ObjectMapper myObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
// This where you enable default enum feature
objectMapper.configure(DeserializationFeature. READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
return objectMapper;
}
I was able to solve it using JsonCreator placed in the ENUM class.
#JsonCreator
public static Type getByValue(String t) {
return Arrays.stream(Type.values())
.filter(a -> a.name().equals(t)).findFirst().orElse(Type.UNKNOWN);
}
}

Build Jacksons ObjectMapper in particular case when global settings are configured

In my application I configured Jackson to use SerializationFeature.WRAP_ROOT_VALUE and DeserializationFeature.UNWRAP_ROOT_VALUE globally.
#Configuration
public class AppConfig {
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE, DeserializationFeature.UNWRAP_ROOT_VALUE);
return builder;
}
}
This configuration works fine but now I am in a situation where in deserialization case I get a JSON Response without rootname. So I have got a Service Class which builds a RestTemplate using RestTemplateBuilder and POST some Data to a REST-Webservice.
#Service
public class ApiServiceImpl
implements ApiService<RegisterResponse> {
private RestTemplate restTemplate;
public ApiServiceImpl(RestTemplateBuilder restTemplateBuilder) {
restTemplate = restTemplateBuilder
.errorHandler(new RestTemplateResponseErrorHandler()).build();
}
#Override
public ResponseEntity<RegisterResponse> callAPI(String requestAsJson,
String username, String password) {
ResponseEntity<RegisterResponse> result = null;
HttpHeaders headers = getHeaders(username, password);
result = restTemplate.exchange(uri, HttpMethod.POST,
new HttpEntity<String>(requestAsJson, headers),
RegisterResponse.class);
return result;
}
}
The Response looks like the following:
{
"redirect-url": "https://any-url.com/?with=params"
}
And I want to deserialize this to the following POJO directly. (Like in restTemplate.exchange configured)
public class RegisterResponse {
#JsonProperty("redirect-url")
private String redirectUrl;
//getter/setter
}
It's clear to get this exception because of the UNWRAP_ROOT_VALUE Feature:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name 'redirect-url' does not match expected ('RegisterResponse') for type [simple type, class xxx.xxx.xxxservice.xxx.model.response.entity.RegisterResponse]
at [Source: (String)"{
"redirect-url": "https://any-url.com/?with=params"
}"; line: 2, column: 5]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1356)
at com.fasterxml.jackson.databind.ObjectMapper._unwrapAndDeserialize(ObjectMapper.java:4087)
How can I configure Jackson to dont use DeserializationFeature.UNWRAP_ROOT_VALUE in this particular case?
Like JB Nizet commented, its possibly by setting a new Instance of MappingJackson2HttpMessageConverter and ObjectMapper for Jakson to the list of MessageConverters.
restTemplate.getMessageConverters().add(getCustomConverter());
private MappingJackson2HttpMessageConverter getCustomConverter() {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
MappingJackson2HttpMessageConverter customConverter =
new MappingJackson2HttpMessageConverter(mapper);
if (!restTemplate.getMessageConverters()
.removeIf(MappingJackson2HttpMessageConverter.class::isInstance)) {
new RuntimeException("Custom MappingJackson2HttpMessageConverter not found");
}
return customConverter;
}

Spring Controller #RequestMapping does not support for non English special characters

I am currently working on an old spring app which exposes REST API for mobile devices. I need to create a new REST service for a Json request with multiple language support. But when I testing this it only support English. When I try with an other language it gives me ????? characters for relevant data.
following is my configuration class
#Configuration
#EnableScheduling
#ComponentScan(basePackages = "lk.test.com.controller")
public class ControllerConfig extends WebMvcConfigurationSupport {
private static final Logger log = LoggerFactory.getLogger(ControllerConfig.class);
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(getMappingJackson2HttpMessageConverter());
addDefaultHttpMessageConverters(converters);
}
#Bean
public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(getObjectMapper());
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(mediaTypes);
return converter;
}
#Bean
public ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return objectMapper;
}
}
Following is a sample controller method
#RequestMapping(value = "send/push", method = RequestMethod.POST,consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
#ResponseBody
public Response sendPush(final #RequestBody CustomerExternalRequest request, HttpServletRequest servletRequest) {
System.out.println(request.getPushMessage());
Response response = new Response();
return response;
}
and Relevant Request Object
#Getter
#Setter
#ToString
public class CustomerExternalRequest extends Request{
private String phoneNo;
//push send
private Boolean isOnlyToCurrenltyLoggedDevice;
private String pushMessage;
private String pushTitle;
}
my Json Request
{"request":{"phoneNo":"+94776587745","isOnlyToCurrenltyLoggedDevice":true,"pushMessage":"测试","pushTitle":"测试"}}
And this is my POSTMAN request
System.out.println(request);
prints ?? in the console
Can any one know how to solve it? Thanks.
Finally found the reason and the solution.
When I tried with a spring boot sample app it worked. Because it is has an embedded tomcat server. In my case I used eclipse jetty plugin. I then found a place to change encoding type and changed it to UTF-8 and now it works fine.
See the configuration screen shot here

Using different jackson ObjectMappers for seperate RequestMapping

I have a single #RequestMapping that consumes a custom MIME type. The request uses an ObjectMapper bean defined in the #Configuration to enabled the JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.
This feature allows typically invalid json (treating backslashes as a non-special character) to be consumed, which is a requirement of this particular #RequestMapping to allow google encoded polylines to be parsed directly. However this means that this ObjectMapper is now being used for ALL of my #RequestMapping when it is really only a requirement for one.
Is there a way to differentiate the ObjectMapper being used for each #Controller or #RequestMapping?
Object Mapper Bean
#Bean
public ObjectMapper objectMapper() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(
JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
return builder.build();
}
Request Mapping Interface Method
#ApiOperation(value = "Returns the toll cost breakdown for a journey", notes = "", response = TotalCost.class, tags={ "pricing", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful operation", response = TotalCost.class) })
#RequestMapping(value = "/pricing/journeyCost",
produces = { "application/json" },
consumes = { "application/vnd.toll-pricing+json" },
method = RequestMethod.POST)
ResponseEntity<TotalCost> getTollBreakdownFromEncodedPoly(#ApiParam(value = "Request object representing the journey" ,required=true ) #RequestBody Journey body);
I found the answer in another stackoverflow question linked to me by another user - https://stackoverflow.com/a/45157169/2073800
I just had to add the following #Bean to my #Configuration
#Bean
public HttpMessageConverters customConverters() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(
JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
final AbstractJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("application/vnd.toll-pricing+json")));
return new HttpMessageConverters(converter);
}
If you've a custom MIME type, then you can register a custom HttpMessageConverter that uses a special ObjectMapper for your MIME type, and returns false from canRead/canWrite for regular MIME types. You register your custom HttpMessageConverter like so:
#EnableWebMvc
#Configuration
#ComponentScan
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
messageConverters.add(myCustomMessageConverter());
super.configureMessageConverters(converters);
}
}
Think of it as related to content negotiation, and not related to URL mapping; URL mapping (#RequestMapping) is meant for finding a handler, not for choosing what marshaller/unmarshaller to use.

Categories