Reputation: 327
I have a Jersey web service that produces/consumes JSON, and uses Jackson for POJO serialization/deserialization. I currently have a client based on the Jersey API, and a Groovy client based on the Grails rest-client-builder plugin. While the Jersey client seems to have no problems, my Grails plugin based client is having issues deserializing the response on a post request I'm working on. Based on the stack trace, it seems like it's trying to serialize the JSON in the response body to a string for some reason?
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@239b0f9d; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@239b0f9d; line: 1, column: 1]
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:228)
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.read(MappingJackson2HttpMessageConverter.java:220)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:795)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:779)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:559)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:520)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:463)
Here is the relevant code for the resource in my Jersey Service
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addStory(Story s) {
if (null != s) {
//underlying DynamoDB client saves data to table
client.insertStory(s);
Response resp = Response.status(200).entity(s).build();
return resp;
}
else{
return Response.status(404).entity("No story data submitted").build();
}
}
Here is the same for the Jersey API based client- working fine.
public class StoryServiceJerseyClient {
private static final Logger logger = LogManager.getLogger(StoryServiceJerseyClient.class.getName());
private static Client client = ClientBuilder.newClient().register(JacksonFeature.class);
WebTarget target = client.target("http://localhost:8080/jerseyexample/rest").path("/story");
public Story createStory(Story s){
Story storyPersisted = null;
storyPersisted = target
.request()
.post(Entity.entity(s, MediaType.APPLICATION_JSON),
Story.class);
return storyPersisted;
}
}
Now the Grails client. I should note here that the Story class referred to is not the same class as that in the service/client above. For the most part it contains the same code, and exists as a temporary workaround for something else I was checking out. But I don't think this has anything to do with the problem, because everything seems to work fine up until processing the response (so the types seem to successfully be going through serialization and converting correctly). The service saves the data sent to a DynamoDB table, and I see that happening successfully. I don't understand why it's trying to serialize the response to a String, I would think would be trying to create a JSON object since the accept type is "application/json".
def addStory(Story story){
def restTemplate = new RestTemplate();
//experimented with adding/removing these/the explicit restTemplate
//but it hasn't had an effect
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter())
restTemplate.getMessageConverters().add(new FormHttpMessageConverter())
restTemplate.getMessageConverters().add(new StringHttpMessageConverter())
def rest = new RestBuilder(restTemplate)
def resp = rest.post(STORY_RESOURCE_URI ){
accept("application/json")
contentType("application/json")
body(story)
}
def json = resp.json
return json
}
BuildConfig.groovy
dependencies {
...
compile "com.fasterxml.jackson.core:jackson-databind:2.5.1"
compile "com.fasterxml.jackson.core:jackson-core:2.5.1"
}
plugins {
...
compile ":rest-client-builder:2.1.0"
}
I'm using Grails 2.4.4, and Jersey 2.17.
UPDATE One thing I'm also noticing while trying to debug this- none of my references to json content type or message converters seems to make a difference. I'm getting the exact same response when I change any combination of the following in the Grails client:
None of this seems to matter. Regardless of what I do, the code seems to realize it's getting JSON and tries to deserialize. I'm really unclear on why it's trying to deserialize to String though. There's no error visible on the server side, and since the post can'd complete, I don't know if/how it's possible to inspect the response I'm getting.
Upvotes: 2
Views: 1896
Reputation: 9
I got the same error in my code (grails version 2.3.11 and rest-client-builder version 2.0.0).
I upgraded the plugin version to "rest-client-builder:2.1.1" and the bug is gone.
Upvotes: 1