RestClientException: Could not extract response. no suitable HttpMessageConverter found - json

Using the curl command:
curl -u 591bf65f50057469f10b5fd9:0cf17f9b03d056ds0e11e48497e506a2 https://backend.tdk.com/api/devicetypes/59147fd79e93s12e61499ffe/messages
I am getting a JSON response:
{"data":[{"device":"18SE62","time":1494516023,"data":"3235","snr":"36.72",...
I save the response on a txt file and parse it using jackson, and everything is fine
ObjectMapper mapper = new ObjectMapper();
File f = new File(getClass().getResource
("/result.json").getFile());
MessageList messageList = mapper.readValue(f, MessageList.class);
and I assume I should get the same result using RestTemplate but that's not the case
RestTemplate restTemplate = new RestTemplate();
MessageList messageList =
restTemplate.getForObject("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef#backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages", MessageList.class);
I got an error instead
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:27)
I tried to set the contentType:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
MessageList messageList =
restTemplate.getForObject(url, entity, MessageList.class);
but then I got a compilation error
The method getForObject(String, Class<T>, Object...) in the type RestTemplate is not applicable for the arguments (String, HttpEntity<String>,
Class<MessageList>)
I also tried to add a the Jackson Message converter
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
//Add the Jackson Message converter
messageConverters.add(new MappingJackson2HttpMessageConverter());
//Add the message converters to the restTemplate
restTemplate.setMessageConverters(messageConverters);
MessageList messageList =
restTemplate.getForObject(url, MessageList.class);
But then I got this error:
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:51)
I also tried adding the class
#Configuration
#EnableWebMvc
public class MvcConf extends WebMvcConfigurationSupport {
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
addDefaultHttpMessageConverters(converters);
}
#Bean
MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter converter
= new MappingJackson2HttpMessageConverter();
return converter;
}
}
but I got the error:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)

The main problem here is content type [text/html;charset=iso-8859-1] received from the service, however the real content type should be application/json;charset=iso-8859-1
In order to overcome this you can introduce custom message converter. and register it for all kind of responses (i.e. ignore the response content type header). Just like this
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
//Add the Jackson Message converter
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// Note: here we are making this converter to process any kind of response,
// not only application/*json, which is the default behaviour
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);

While the accepted answer solved the OP's original problem, most people finding this question through a Google search are likely having an entirely different problem which just happens to throw the same no suitable HttpMessageConverter found exception.
What happens under the covers is that MappingJackson2HttpMessageConverter swallows any exceptions that occur in its canRead() method, which is supposed to auto-detect whether the payload is suitable for json decoding. The exception is replaced by a simple boolean return that basically communicates sorry, I don't know how to decode this message to the higher level APIs (RestClient). Only after all other converters' canRead() methods return false, the no suitable HttpMessageConverter found exception is thrown by the higher-level API, totally obscuring the true problem.
For people who have not found the root cause (like you and me, but not the OP), the way to troubleshoot this problem is to place a debugger breakpoint on onMappingJackson2HttpMessageConverter.canRead(), then enable a general breakpoint on any exception, and hit Continue. The next exception is the true root cause.
My specific error happened to be that one of the beans referenced an interface that was missing the proper deserialization annotations.
UPDATE FROM THE FUTURE
This has proven to be such a recurring issue across so many of my projects, that I've developed a more proactive solution. Whenever I have a need to process JSON exclusively (no XML or other formats), I now replace my RestTemplate bean with an instance of the following:
public class JsonRestTemplate extends RestTemplate {
public JsonRestTemplate(
ClientHttpRequestFactory clientHttpRequestFactory) {
super(clientHttpRequestFactory);
// Force a sensible JSON mapper.
// Customize as needed for your project's definition of "sensible":
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.configure(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter() {
public boolean canRead(java.lang.Class<?> clazz,
org.springframework.http.MediaType mediaType) {
return true;
}
public boolean canRead(java.lang.reflect.Type type,
java.lang.Class<?> contextClass,
org.springframework.http.MediaType mediaType) {
return true;
}
protected boolean canRead(
org.springframework.http.MediaType mediaType) {
return true;
}
};
jsonMessageConverter.setObjectMapper(objectMapper);
messageConverters.add(jsonMessageConverter);
super.setMessageConverters(messageConverters);
}
}
This customization makes the RestClient incapable of understanding anything other than JSON. The upside is that any error messages that may occur will be much more explicit about what's wrong.

I was having a very similar problem, and it turned out to be quite simple; my client wasn't including a Jackson dependency, even though the code all compiled correctly, the auto-magic converters for JSON weren't being included. See this RestTemplate-related solution.
In short, I added a Jackson dependency to my pom.xml and it just worked:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>

One way to debug this issue is to first take the response as a String.class and then use
Gson().fromJson(StringResp.body(), MyDTO.class)
It will most likely still fail, but this time it will throw the fields that caused the error in the first place. Following the modification, we can resume using the previous approach as before.
ResponseEntity<String> respStr = restTemplate.exchange(URL,HttpMethod.GET, entity, String.class);
Gson g = new Gson();
The following step will generate an error with the fields that are causing the problem.
MyDTO resp = g.fromJson(respStr.getBody(), MyDTO.class);
I don't have the error message with me, but it will point to the problematic field and explain why. Resolve those and try the previous approach again.

If the above response by #Ilya Dyoshin didn't still retrieve,
try to get the response into a String Object.
(For my self thought the error got solved by the code snippet by Ilya, the response retrieved was a failure(error) from the server.)
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
ResponseEntity<String> st = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
And Cast to the ResponseObject DTO (Json)
Gson g = new Gson();
DTO dto = g.fromJson(st.getBody(), DTO.class);

In my case #Ilya Dyoshin's solution didn't work: The mediatype "*" was not allowed.
I fix this error by adding a new converter to the restTemplate this way during initialization of the MockRestServiceServer:
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
Arrays.asList(
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
mockServer = MockRestServiceServer.createServer(restTemplate);
(Based on the solution proposed by Yashwant Chavan on the blog named technicalkeeda)
JN Gerbaux

You need to create your own converter and implement it before making a GET request.
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);

This way you can get the object response using resttemplate and set contentType using MediaType.APPLICATION_JSON
public List<Employee> getListofEmployee()
{
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<List<Employee>> response = restTemplate.exchange("http://hello-server/rest/employees",
HttpMethod.GET,entity, new ParameterizedTypeReference<List<Employee>>() {});
return response.getBody(); //this returns List of Employee
}

Please add the shared dependency having jackson databind package . Hope this will clear the issue.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>

In my case it was caused by the absence of the jackson-core, jackson-annotations and jackson-databind jars from the runtime classpath.
It did not complain with the usual ClassNothFoundException as one would expect but rather with the error mentioned in the original question.

Spring sets the default content-type to octet-stream when the response is missing that field. All you need to do is to add a message converter to fix this.

Other possible solution : I tried to map the result of a restTemplate.getForObject with a private class instance (defined inside of my working class). It did not work, but if I define the object to public, inside its own file, it worked correctly.

I was trying to use Feign, while I encounter same issue, As I understood HTTP message converter will help but wanted to understand how to achieve this.
#FeignClient(name = "mobilesearch", url = "${mobile.search.uri}" ,
fallbackFactory = MobileSearchFallbackFactory.class,
configuration = MobileSearchFeignConfig.class)
public interface MobileSearchClient {
#RequestMapping(method = RequestMethod.GET)
List<MobileSearchResponse> getPhones();
}
You have to use Customer Configuration for the decoder, MobileSearchFeignConfig,
public class MobileSearchFeignConfig {
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
#Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new MappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
#Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}
public class MappingJackson2HttpMessageConverter extends org.springframework.http.converter.json.MappingJackson2HttpMessageConverter {
MappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}
}

In my case i was missing the No Args contructor
#Data
#AllArgsConstructor
#NoArgsConstructor
for those who are no using Lombok do add no args constructor in the mapping pojo
public ClassA() {
super();
// TODO Auto-generated constructor stub
}
also dont forget to add the Bean of Restemplate in main file if you are using the same

Infuriating problem right?
You just wanna get the result of the call, and you have a deSerialization error...that you have no clue where to look for.
Well, all is not lost.
If you change the type of call to String, you can get the JSON equivalent and then write a test to see why it is not serializing:
RestTemplate restTemplate = new RestTemplate();
String messageListString = restTemplate.getForObject("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef#backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages", String.class);
Here is an example with an input param I used in my Kotlin project:
fun givenCUT_whenFetchingBillableItemsForAPastMonthWithoutBillingData_thenWeSucceedInGettingAnEmptyXmlResponse() {
val restTemplate = RestTemplate()
val uri = "http://localhost:$port/api/test/billing/xml/month/{month}/"
val params: MutableMap<String, String> = HashMap()
params["month"] = "2022-09-01"
val stringResponse = restTemplate.getForObject(uri, String::class.java, params)
assertNotNull(stringResponse)
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<bi:billableItems xmlns:bi=\"urn:blahblahblah\"/>\n", stringResponse)
}
If you step through that test, and harvest the actual JSON of your endpoint, you can then use a test like this, to pump it in, and see why Jackson or Gson is complains:
#Test
fun givenCUT_whenDeSerializingBEStateCorrectionsResponse_thenWeGetAnInstanceOfAListOfBillingOrdersSuccessfully() {
//Raw JSON harvested from BillingOrderControllerTest
val harvestedFEJsonBillingOrderList = "YOUR JSON Harvested from above goes here"
val mapper = ObjectMapper()
mapper.registerModule(JavaTimeModule())
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
val deSerBillingOrderList = mapper.readValue(harvestedFEJsonBillingOrderList, Array<BillingOrder>::class.java)
assertNotNull(deSerBillingOrderList)
assertEquals(1, deSerBillingOrderList.size)
}
The post is just as easy...the following is a snippet but you can see I finally gave up and commented out the serialization error part, and reverted to the String version and did the necessary with the test above in Jackson; I just this minute did that, and found 4 issues in the de-serialized JSON that Jackson explicitly reported on, and that I will fix. Then, I will revert the below to the typed version and it should have solved the problem:
try {
val result = restTemplate!!.postForEntity(uri, billingOrders, String::class.java)
/* val result = restTemplate!!.postForObject(
baseUrl,
billingOrders,
ResponseEntity<List<BillingOrder>>::class.java)*/
assertNotNull(result)
} catch (e: Exception) {
log.error("Failed restTemplate.postForObject with $e")
}

Related

Spring API application/json is not working when getting text/json content type

I'm querying a PhraseApp API to get translations and setting the content type on the headers as appplication/json:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
But the API sends a response as attachment with content type text/json; charset=UTF-8. And Spring raises the error:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.String] and content type [text/json;charset=UTF-8]
The response I get is in the following form:
{
"some.key.a": "some translation for a",
"some.key.b": "some translation for b",
"some.key.c": "some translation for c"
...
}
I expected to assign the response to a String variable and parse it later.
Any idea on what is going wrong here ? Thank you.
according to the JSON-specification the content type of JSON should always be "application/json".
One possible fix would be to edit the request and set the correct content type.
Fortunately I am from the PhraseApp Team and could simply fix this on our side ;)
You should be able to use it with your original code now!
If you need further help, send me a PN or an email at vincent#phraseapp.com
I had to use the following steps to get it works:
Use /api/v2/projects/{projectId}/locales/{localeId}/download
Create a custom converter as follows:
public class PhraseTextJsonConverter extends MappingJackson2HttpMessageConverter {
public PhraseTextJsonConverter() {
setSupportedMediaTypes(initMediaTypes());
}
private List<MediaType> initMediaTypes() {
return Arrays.asList(new MediaType("text", "json", DEFAULT_CHARSET));
}
}
Add converters to the template:
protected static RestTemplate createRestTemplateWithConverter() {
final RestTemplate restTemplate = new RestTemplate();
final List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<>();
httpMessageConverters.add(new MappingJackson2HttpMessageConverter());
httpMessageConverters.add(new ByteArrayHttpMessageConverter());
restTemplate.setMessageConverters(httpMessageConverters);
return restTemplate;
}
and later a custom one:
private void addPhraseTextJsonConverter() {
restTemplate.getMessageConverters().add(new PhraseTextJsonConverter());
}
To be able to get all the translations as a Map, I had to use ParameterizedTypeReferenceas follows:
protected ResponseEntity<HashMap<String, String>> requestPhraseApp(final HttpEntity<Object> requestEntity, final URI uri) {
ParameterizedTypeReference<HashMap<String, String>> typeRef = new ParameterizedTypeReference<HashMap<String, String>>() {};
...
return restTemplate.exchange(uri, HttpMethod.GET, requestEntity, typeRef);
...
}
Do not forget to init the request parameters to get all the translations in a Map:
List urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("file_format", "simple_json"));
That's it.

jacksonMessageConverter corrupt mp3 file as octet encode response

I have a spring MVC config with the following:
public class SpringConfiguration extends WebMvcConfigurerAdapter {
public MappingJackson2HttpMessageConverter jacksonMessageConverter() {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
//Registering Hibernate4Module to support lazy objects
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//Here we add our custom-configured HttpMessageConverter
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
}
The previous method used to ignore all lazy relation without adding JsonIgnore in model
The problem is I have a route to steam mp3 file as an octet response as following
#GetMapping(value = "/audio/{id}")
public ResponseEntity<byte[]> streamMp3FileToAdmin(#PathVariable Integer id) {
CorporateCampaign camp = corporateCampaignService.findById(id);
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(Utilities.getFileAsBytes(camp.getVoiceUrl()),httpHeaders,HttpStatus.OK);
}
If I remove jackson message converter the steaming works fine but when I add jackson message converter the stream doesn't work any more
I read this question Spring MVC: How to return image in #ResponseBody?
and a lot but I didn't find a solution yet
You need to add produces = MediaType.APPLICATION_OCTET_STREAM to the #GetMapping(value = "/audio/{id}") to specify produced result content type and let browser recognize it properly.

RestEasy, how to prevent marshalling of JAXB annotated POJOs to and from JSON

I started with a service that consumes and produces output in JSON. I use the resteasy-jackson-provider for (de)marshalling which takes its information from the class description. After a while I was asked to add XML as MediaType. So I annotated my DTOs with JAXB annotations and added the resteasy-jaxb-provider. As a result, I observed that the produced JSON output derives from the JAXB annotations which differs from the original format.
I am on RestEasy Version 3.0.4. As described I use the following providers
resteasy-jackson-provider
resteasy-axb-provider.
resteasy-jettison-provider, because I integrated RestEasy into Spring and this provider is a transitive dependency.
I got aware of the problem when I
used XmlElementWrapper for lists and when
I wrote a custom XmlAdapter which serializes a complex data structure Map<String, List<String>>. Requests with XML MediaType are fine. Requests with JSON MediaType cause an exception. Jackson seems to exploit the XmlAdapter for further information. This was not the case before. Jackson was able to marshall the Map without the JAXB annotations.
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "customer" (Class x.y.z.OptionalParametersMapType), not marked as ignorable
at [Source: org.apache.catalina.connector.CoyoteInputStream#77119553; line: 1, column: 131] (through reference chain: x.y.z.Request["optional"]->x.y.zOptionalParametersMapType["customer"]
)
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:673)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:659)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1365)
at org.codehaus.jackson.map.deser.BeanDeserializer._handleUnknown(BeanDeserializer.java:725)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:703)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.xc.XmlAdapterJsonDeserializer.deserialize(XmlAdapterJsonDeserializer.java:59)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:299)
at org.codehaus.jackson.map.deser.SettableBeanProperty$FieldProperty.deserializeAndSet(SettableBeanProperty.java:579)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:697)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
So, how can I prevent RestEasy from using the JAXB annotations for marshalling to and from JSON?
Here is the request class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domainRecommendationRequest")
public class Request {
#XmlJavaTypeAdapter(OptionalParametersXmlAdapter.class)
private Map<String, List<String>> optional = new HashMap<>();
}
Here is the XmlAdapter:
#Override
public class OptionalParametersXmlAdapter extends XmlAdapter<OptionalParametersMapType, Map<String, List<String>>> {
public OptionalParametersMapType marshal(Map<String, List<String>> v) throws Exception {
OptionalParametersMapType result = new OptionalParametersMapType();
List<OptionalParameterItemType> optionalParameterItemTypes = new ArrayList<>();
Set<String> keySet = v.keySet();
for (String parameterName : keySet) {
OptionalParameterItemType item = new OptionalParameterItemType();
item.name = parameterName;
item.values = v.get(parameterName);
optionalParameterItemTypes.add(item);
}
result.parameter = optionalParameterItemTypes;
return result;
}
}
Here is the wrapper for the map:
public class OptionalParametersMapType {
public List<OptionalParameterItemType> parameter = new ArrayList<>();
}
Here is the actual map entry item:
public class OptionalParameterItemType {
#XmlAttribute
public String name;
#XmlElementWrapper(name = "values")
#XmlElement(name = "value")
public List<String> values = new ArrayList<>();
}
This is what I expect in the JSON request:
{"optional":{"customer":["Mike"]}}
As you can see, I do intend to have a different format in XML.
The problem is resteasy-jackson-provider depends on jackson-module-jaxb-annotations, which is used to map JAXB annotations/annotated classes to JSON. Now in a normal explicit use of ObjectMapper, in order to make use of this module, we would need to explicitly register this module like (See here)
ObjectMapper objectMapper = new ObjectMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
objectMapper.registerModule(module);
-- OR --
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
objectMapper.setAnnotationIntrospector(introspector);
That being said, it appears (not confirmed with any facts, but looks probable) that when the ObjectMapper is being created for your serialization, when the JAXB annotations are noticed, the module is automatically registered.
I don't know of any possible annotations we can use to stop this, but one way to solve this problem is to create a ContextResolver for the ObjectMapper, where we don't register the JAXB module.
#Provider
public class ObjectMapperContextResolver
implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(Class<?> type) {
return new ObjectMapper();
}
}
Once we register that with our JAX-RS application, it will be the context resolver used to get the ObjectMapper. We could configure the ObjectMapper further, but this is just an example. Test it and it works as expected.

Jersey + Jackson + arbitrary json

I am using Jersey + Jackson + Guice for my webapp. Now I wanted to implemented a simple REST call for my client where i receive arbitrary JSON data on the server, but every time i get the following exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "validTo" (Class org.codehaus.jettison.json.JSONObject), not marked as ignorable| at [Source: org.eclipse.jetty.server.HttpConnection$Input#1cafa346; line: 1, column: 25] (through reference chain: org.codehaus.jettison.json.JSONObject["validTo"])
My method signature looks like the following:
#Override
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void post(JSONObject json) throws JSONException {
}
My Guice config:
return Guice.createInjector(new TTShiroModule(this.servletContext), ShiroWebModule.guiceFilterModule(),
new ServiceModule(), new JerseyServletModule() {
#Override
protected void configureServlets() {
bind(GuiceContainer.class);
bind(MessageBodyReader.class).to(JacksonJsonProvider.class);
bind(MessageBodyWriter.class).to(JacksonJsonProvider.class);
serve("/rest/*").with(GuiceContainer.class, params);
}
#Provides
#Singleton
ObjectMapper objectMapper() {
final ObjectMapper mapper = new ObjectMapper();
return mapper;
}
#Provides
#Singleton
JacksonJsonProvider jacksonJsonProvider(ObjectMapper mapper) {
return new JacksonJsonProvider(mapper);
}
});
I searched for this exception a long time but couldnt find any help. I also tried different approaches but wasnt able to resolve this issue.
Anyone can help me?
If you need more information, then please let me know!
best regards.
Jersey won't automatically unwrap the json string to JSONObject on its own, but you could easily do as follows:
#Override
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void post(String json) throws JSONException {
JSONObject object = new JSONObject(json);
// do things with object
}

How to send JSON in Spring?

I tried to find how I can write in Spring to POST JSON from REST client. For example, I wrote:
#RequestMapping(value = "/{userId}/add", method = RequestMethod.POST, headers = {"content-type=application/json"})
#ResponseBody
public Map<String, String> saveUser(#RequestBody User user, BindingResult result) {
Map<String, String> jsonResponse = new HashMap<String, String>();
if (result.hasErrors()) {
jsonResponse.put("Message", "Can't add the user");
jsonResponse.put("Code", "401");
return jsonResponse;
}
userService.addUser(user);
jsonResponse.put("Message", "Success add User");
jsonResponse.put("Code", "200");
return jsonResponse;
}
End tested it from Firefox REST client. But I saw 404 error. What am I doing wrong? Thanx for help.
First, if the URI of your request ended with "/user/2/add", it won't map to your method, which is mapped as "/{userId}/add". This will cause the HTTP 404 error you receive. Instead, your URI should end with "/2/add", if the "userId" is 2.
Second, annotating the User parameter with #RequestBody is not enough for the complex User type. You will need to convert your JSON request body into a User object. You can accomplish this with the MappingJacksonHttpMessageConverter. By declaring a bean of this type, you can use Jackson's annotations to control how the JSON is parsed into the User properties.