Map nested json to a pojo with raw json value - java

I have a nested json pojo where the nested part of json is tagged with #JsonRawValue. I am trying it to map with rest template, but I am getting the error
JSON parse error: Cannot deserialize instance of java.lang.String out of START_OBJECT token;
The nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException.
This is what my response object looks like:
import com.fasterxml.jackson.annotation.JsonRawValue;
public class ResponseDTO {
private String Id;
private String text;
#JsonRawValue
private String explanation;
//getters and setters;
}
where explanation is a json mapped to a string. This works fine with postman, swagger, and I see the explanation as json in the response.
But when I am testing it using Rest Template:
ResponseEntity<ResponseDTO> resonseEntity = restTemplate.exchange(URI, HttpMethod.POST, requestEntity, ResponseDTO.class);
I see this exception:
org.springframework.web.client.RestClientException: Error while extracting
response for type [class com.**.ResponseDTO] and content type
[application/json;charset=utf-8]; nested exception is
org.springframework.http.converter.HttpMessageNotReadableException: JSON
parse error: Cannot deserialize instance of java.lang.String out of
START_OBJECT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 604] (through
reference chain: com.****.ResponseDTO["explanation"])

Jackson is telling you that it can't insert an Object (in the error log) inside a String.
The #JsonRawValue is used during serialization of objects to JSON format. It is a way to indicate that the String field is to be sent as-is. In other words, the purpose is to tell Jackson that the String is a valid JSON and should be sent without escaping or quoting.
What you can do instead is provide Jackson with a custom method for it to set the field value. Using JsonNode as the argument will force Jackson to pass the "raw" value. From there you can get the string representation:
public class ResponseDTO {
private String Id;
private String text;
private String explanation;
//getters and setters;
#JsonProperty("explanation")
private void unpackExplanation(JsonNode explanation) {
this.explanation = explanation.toString();
}
}

Related

Cannot deserialize instance of java.lang.String out of START_ARRAY token:

Hi Everyone i want to call my api from lambda but when i am passing the request i got this error "Cannot deserialize instance of java.lang.String out of START_ARRAY token"
This is the POJO but here only List aids is not deserializing when passing request from Lambda
#Data
#Builder
#ToString
public class BulkFetchRequest {
#JsonProperty("merchId")
private String merchId;
#JsonProperty("markId")
private String markId;
#JsonProperty("aids")
private List<String> aids;
}
This is my request format:
{
"merchId": "2077759361",
"markId" : "44571",
"aids": ["B096BK8GW", "B09B7S84X", "B07N7QPZD", "B09GG2LPL" , "B09M41GVR"]
}

Java Spring JSON parse error: Cannot deserialize instance out of START_ARRAY token

I have a method with a restTemplate call like this:
restTemplate.getForObject(apiUrl ,Someclass.class);
Someclass.class:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Imp implements Serializable {
#JsonProperty("Id")
private String Id;
#JsonProperty("ReportId")
private String ReportId;
#JsonProperty("Title")
private String Title;
#JsonProperty("Name")
private String Name;
#JsonProperty("Uri")
private String Uri;
}
The API returns an array, and the error i'm receiving is:
org.springframework.web.client.RestClientException: Error while extracting response for type [class ...] and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of com... out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of com... out of START_ARRAY token
Which restTempalte method shoud i use to get proper api response?, or where is the problem?.thanks!
You said the API returns an array.
But your line of code restTemplate.getForObject(apiUrl ,Someclass.class);
will work only for a single Someclass object.
You should use new ParameterizedTypeReference<List<Someclass.class>> along with the exchange method.
Refer to the below link
Get list of JSON objects with Spring RestTemplate

Ignore a java bean field while converting to jSON

Ignore a java bean field while converting to jSON
I am having a java bean and sending JSON as response , In that java bean I want to have
some transient fields , that should not come into JSON .
#XmlRootElement(name = "sample")
class Sample{
private String field1;
#XmlTransient
private String transientField;
//Getter and setters
public String toJSON() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(this);
return json;
}
}
When I am calling toJSON method I am still getting "transientField" in JSON.
And I have a get rest API that returns this Sample JSON as response.
#GET
#Path("/somePath/")
#Produces({"application/json"})
Sample getSample();
In this response also I am getting that transient field .
Am I doing something wrong? Please help me to do this .
Try using #JsonIgnore instead.
method 1: use annotation #JsonIgnoreProperties("fieldname") to your POJO
example : #JsonIgnoreProperties(ignoreUnknown = true, value = {"fieldTobeIgnored"})
method 2:#JsonIgnore for a specific field that is to be ignored deserializing JSON

JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token

I wanted to write a small and simple REST service using Spring Boot.
Here is the REST service code:
#Async
#RequestMapping(value = "/getuser", method = POST, consumes = "application/json", produces = "application/json")
public #ResponseBody Record getRecord(#RequestBody Integer userId) {
Record result = null;
// Omitted logic
return result;
}
The JSON object I sent is the following:
{
"userId": 3
}
And here is the exception I got:
WARN 964 --- [ XNIO-2 task-7]
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP
message:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: Can not deserialize instance of
java.lang.Integer out of START_OBJECT token at [Source:
java.io.PushbackInputStream#12e7333c; line: 1, column: 1]; nested
exception is com.fasterxml.jackson.databind.JsonMappingException: Can
not deserialize instance of java.lang.Integer out of START_OBJECT
token at [Source: java.io.PushbackInputStream#12e7333c; line: 1,
column: 1]
Obviously Jackson can not deserialize the passed JSON into an Integer. If you insist to send a JSON representation of a User through the request body, you should encapsulate the userId in another bean like the following:
public class User {
private Integer userId;
// getters and setters
}
Then use that bean as your handler method argument:
#RequestMapping(...)
public #ResponseBody Record getRecord(#RequestBody User user) { ... }
If you don't like the overhead of creating another bean, you could pass the userId as part of Path Variable, e.g. /getuser/15. In order to do that:
#RequestMapping(value = "/getuser/{userId}", method = POST, produces = "application/json")
public #ResponseBody Record getRecord(#PathVariable Integer userId) { ... }
Since you no longer send a JSON in the request body, you should remove that consumes attribute.
Perhaps you are trying to send a request with JSON text in its body from a Postman client or something similar like this:
{
"userId": 3
}
This cannot be deserialized by Jackson since this is not an Integer (it seems to be, but it isn't). An Integer object from java.lang Integer is a little more complex.
For your Postman request to work, simply put (without curly braces { }):
3

RestTemplate: Can not deserialize instance of OBJECT out of START_OBJECT token

I'm trying out the RestTemplate stuff from spring. I'm trying to read in this JSON data: JSON Data. The data is a a key value pair in which the key is "geonames" and the value is an array of "geoname" objects.
I have a Geoname class to handle the input. This class also has getters and setters in it. I then have an app class that just runs a main method to invoke a RestTemplate object:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Geoname {
private String name;
private long lat;
private long lng;
private String countrycode;
}
App.java
public class App
{
public static void main( String[] args )
{
String jsonUrl = "http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo";
RestTemplate template = new RestTemplate();
ResponseEntity<Geoname[]> entity = template.getForEntity(jsonUrl, Geoname[].class);
List<Geoname> data = Arrays.asList(entity.getBody());
System.out.print("Success!");
}
}
This is my error output:
Exception in thread "main" org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:208)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:200)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:812)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:796)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:576)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:529)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:261)
at com.declan.App.main(App.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:835)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:831)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.handleNonArray(ObjectArrayDeserializer.java:232)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:139)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:17)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2660)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:205)
... 13 more
I've tried this setup, i've tried using getForObject like on the spring documentation, I've tried searching here on stackoverflow and using the common answer of mapping to a list. I've even tried creating a Geonames class that contained just an array or Geoname objects but that didn't work either...Same error all the time. Perhaps, I'm not reading the Json correctly but if someone could lend me a pair of eyes I'd be grateful. :D
Cheers!
EDIT
Okay, I now have this new class and it now makes the GET request for the JSON data. However, after a debug, the ResponseEntity body has the array set to null. Do I need to instantiate the array in Geonames manually?
#JsonIgnoreProperties(ignoreUnknown = true)
public class Geonames {
#JsonProperty("geonames")
Geoname[] geonames;
public void setGeonames(Geonames[] geonames) {
this.geonames = geonames;
}
public void getGeonames() {
return geonames;
}
}
Resolved. Turns out that the null was because the JSON link had a max hits per hour limit. By creating my own account on the site, the api gave me my own limits. So the response body then populated with data.

Categories

Resources