I am trying to read json in my spring boot project.
My JSON data is as follows:
[{
"userId":"101"
},
{
"partNum":"aaa"
},
{
"partNum":"bbb"
},
{
"partNum":"ccc"
}]
I have created a DTO class:
public class TcPartDto {
private String userId;
private List<String> partNum;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public List<String> getPartNum() {
return partNum;
}
}
And I am calling it in my Controller as follows:
#RequestMapping(value = "/volumeinfo", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody
public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto partList) throws Exception {
return tcService.fetchVolumeInfo(partList);
}
But I get the following error:
Through Postman I get this error:
"Could not read document: Can not deserialize instance of
tc.service.model.TcPartDto out of START_ARRAY token\n at [Source:
java.io.PushbackInputStream#5359141a; line: 1, column: 1]; nested
exception is com.fasterxml.jackson.databind.JsonMappingException: Can
not deserialize instance of tc.service.model.TcPartDto out of
START_ARRAY token\n at [Source: java.io.PushbackInputStream#5359141a;
line: 1, column: 1]"
What wrong am I doing?
The DTO you've created does not match the json data it's trying to read.
Based on your DTO sample json should be:
{
"userId" : "someId",
"partNum" : [ "partNum1", "partNum2"]
}
otherwise if json object you're consuming is fixed then DTO should be:
public class MyDTO {
private String userId;
private String partNum;
// ...
}
and with your controller with a parameter of type
List<MyDTO>
You are sending a JSON Array to your public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto partList) method. But it should be deserialize to a single object: TcPartDto partList.
Change your JSON structure to send only a single TcPartDto or make sure your that your volumeinfo method can receive an Array or List.
And you have to change your JSON structure in case you want to send a single object:
{
"userId": 101,
"partNum": [
"aaa",
"bbb",
"ccc"
]
}
As others already pointed out various answers.
if in case this is the json that you want to map without changing the class :
JSON:
[{
"userId":"101"
},
{
"partNum":"aaa"
},
{
"partNum":"bbb"
},
{
"partNum":"ccc"
}]
Class:
#JsonIgnoreProperties(ignoreUnknown=true)
public class TcPartDto {
private String userId;
private List<String> partNum;
//getters and setters
}
Controller:
#RequestMapping(value = "/volumeinfo", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody
public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto[] partArray) throws Exception {
return tcService.fetchVolumeInfo(partArray);
}
Output:
[{"userId":"101","partNum":null},{"userId":null,"partNum":["aaa"]},{"userId":null,"partNum":["bbb"]},{"userId":null,"partNum":["ccc"]}]
Related
I want to fetchMultiple(ParameterizedTypeReference<List<T>> responseType) for a given List<T>, in this case, I want to get directly a List<Account> but I am getting an error because the list of accounts is encapsulated in another object, as shown below:
{
"accounts": [
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
}
There is some Jackson annotation to filter this somehow in order to be processed like this:
[
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
POJO
#Data
public class Account {
private String accountUid;
private String defaultCategory;
private String currency;
private String createdAt;
}
RestRequestTemplate.java
public List<T> fetchMultiple(ParameterizedTypeReference<List<T>> responseType) {
return new RestTemplate().exchange(this.url, this.httpMethod, this.request, responseType).getBody();
}
AccountsServiceImpl.java
public List<Account> getAccounts() {
RestRequestTemplate restRequestTemplate = new RestRequestTemplate(GET_ACCOUNTS, HttpMethod.GET, Collections.EMPTY_MAP);
return restRequestTemplate.fetchMultiple(new ParameterizedTypeReference<List<Account>>() {});
}
There is indeed an annotation to ignore the root object. It is called #JsonUnwrapped. Annotate your method with that annotation and your json should be without the root object.
Hi i am using retrofit to call my API with spring boot.
API Response
[
{
"name": "whishky",
"price": 1000
},
{
"name": "vodka",
"price": 200
}
]
My pojo class looks like
public class MyResponse {
List<MyObject> resp;
}
And MyObject class looks like
public class MyObject implements Serializable {
#JsonProperty("name")
private String name;
#JsonProperty("price")
private Double price;
}
API call
Call<MyResponse> saveRequestCall = MyApi.fetchData(request);
Response<MyResponse> execute = saveRequestCall.execute();
Now the problem is when i call the API i am getting the exception
2020-04-25 18:08:18,895 ERROR c.s.e.b.XYZServiceImpl Error in fetching datawith exception com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `zzz.MyResponse` out of START_ARRAY token
at [Source: (InputStreamReader); line: 1, column: 1]
Any help will be appreciated regarding how i can parse this.
The problem seems to be mapping to MyResponse because it's excepecting something like
{
"resp": [
{
"name": "whishky",
"price": 1000
},
...
]
}
It should be fixed if you use
Call<List<MyObject>> saveRequestCall = MyApi.fetchData(request);
Response<List<MyObject>> execute = saveRequestCall.execute();
The above json represent JSONArray of JSONObject with two properties name and price, so you don't need to wrap List<MyObject> resp in another class, you can directly deserialize json into List<MyObject>
Call<List<MyObject>> saveRequestCall = MyApi.fetchData(request);
Response<List<MyObject>> execute = saveRequestCall.execute();
I want to serialize a JSON-String I receive as a POJO, for further usage in my code, but I am struggling to get it working without writing a custom serializer.
I would prefer as solution without writing a custom serializer, but if that is the only possible way I will write one.
Additionally I believe the data I receive is a weird JSON since the list I request is not sent as list using [] but rather as a object using {}.
I receive the following list/object (shortened):
{
"results": {
"ALL": {
"currencyName": "Albanian Lek",
"currencySymbol": "Lek",
"id": "ALL"
},
"XCD": {
"currencyName": "East Caribbean Dollar",
"currencySymbol": "$",
"id": "XCD"
},
"EUR": {
"currencyName": "Euro",
"currencySymbol": "â?¬",
"id": "EUR"
},
"BBD": {
"currencyName": "Barbadian Dollar",
"currencySymbol": "$",
"id": "BBD"
},
"BTN": {
"currencyName": "Bhutanese Ngultrum",
"id": "BTN"
},
"BND": {
"currencyName": "Brunei Dollar",
"currencySymbol": "$",
"id": "BND"
}
}
}
I created my first POJO for the inner object like this:
public class CurrencyDTO implements Serializable {
private String currencyName;
private String currencySymbol;
private String currencyId;
#JsonCreator
public CurrencyDTO( #JsonProperty( "currencyName" ) String currencyName, #JsonProperty( "currencySymbol" ) String currencySymbol,
#JsonProperty( "id" ) String currencyId )
{
this.currencyId = currencyId;
this.currencyName = currencyName;
this.currencySymbol = currencySymbol;
}
}
which itself is fine. Now I wrote another POJO as a wrapper for the data a layer above which looks like this:
public class CurrencyListDTO implements Serializable {
private List<Map<String, CurrencyDTO>> results;
public CurrencyListDTO()
{
}
}
Adding the annotations #JsonAnySetter or using the #JsonCreator didn't help either, so I removed them again and now I am wondering which little trick could enable the correct serialization of the json.
My Exception is the following:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token
at [Source: (String)"{"results":{"ALL":{"currencyName":"Albanian Lek","currencySymbol":"Lek","id":"ALL"},"XCD":{"currencyName":"East Caribbean Dollar","currencySymbol":"$","id":"XCD"},"EUR":{"currencyName":"Euro","currencySymbol":"â?¬","id":"EUR"},"BBD":{"currencyName":"Barbadian Dollar","currencySymbol":"$","id":"BBD"},"BTN":{"currencyName":"Bhutanese Ngultrum","id":"BTN"},"BND":{"currencyName":"Brunei Dollar","currencySymbol":"$","id":"BND"},"XAF":{"currencyName":"Central African CFA Franc","id":"XAF"},"CUP":{"cur"[truncated 10515 chars]; line: 1, column: 12] (through reference chain: com.nico.Banking.api.data.dto.CurrencyListDTO["results"])
You should change your CurrencyListDTO to:
public class CurrencyListDTO {
private Map<String, CurrencyDTO> results;
// getters and setters
}
Because the results field in the response object is another object with the currencyId as key and no array.
You then can create your list of currencies like this:
ObjectMapper mapper = new ObjectMapper();
CurrencyListDTO result = mapper.readValue(json, CurrencyListDTO.class);
List<CurrencyDTO> currencies = new ArrayList<>(result.getResults().values());
Your CurrencyListDTO should look like below. results property is a JSON Object which should be mapped directly to Map. You can convert it to Collection using keySet or values methods.
class CurrencyListDTO implements Serializable {
private Map<String, CurrencyDTO> results;
public Map<String, CurrencyDTO> getResults() {
return results;
}
public void setResults(Map<String, CurrencyDTO> results) {
this.results = results;
}
#Override
public String toString() {
return "CurrencyListDTO{" +
"results=" + results +
'}';
}
}
I am trying to send a json long list and take records from db.
My controller is:
#Api(tags = Endpoint.RESOURCE_customer, description = "customer Resource")
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public class CustomerResourceController {
private final customerService customerService;
public CustomerResourceController(customerService customerService) {
this.customerService = customerService;
}
#ApiOperation(
value = "Return customer",
response = customerDto.class, responseContainer="List"
)
#PostMapping(value = Endpoint.RRESOURCE_customer_ID)
public List<customerDto> getCustomersByIds(#RequestBody List<Long> ids) {
return customerService.findcustomerIds(ids);
}
}
and client class is:
#Headers("Content-Type: " + MediaType.APPLICATION_JSON_VALUE)
public interface CustomerClient {
#RequestLine("POST /customer/customers/search")
List<LocGrpDto> getCustomersByIds(#RequestBody #Validated List<Long> ids);
}
And i test this service in postman with JSON:
{ "ids": [1,7,8] }
But I get this error:
{
"timestamp": "2018-10-05T13:29:57.645+0000",
"status": 400,
"error": "Bad Request",
"message": "Could not read document: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token\n at [Source: java.io.PushbackInputStream#3cb8b584; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token\n at [Source: java.io.PushbackInputStream#3cb8b584; line: 1, column: 1]",
"path": "/api/v1/customer/customers/search",
"errors": []
}
What is the problem? Do you see any problem here or it may be caused because of my service class or dto classes ?
Try requesting with the payload [1,7,8], not {"ids": [1,7,8]}.
Your JSON would translate to a request body with the next format.
class Body {
private List<Long> ids;
// constructor, getters and setters
}
For a REST client, you can take a look at RestTemplate.
RestTemplate template;
List<Long> ids;
List<CustomerDto> = template.exchange(
"/customer/customers/search",
HttpMethod.POST,
new HttpEntity<>(ids),
new ParameterizedTypeReference<List<CustomerDto>>() {}).getBody()
I'm using Moshi as converter for Retrofit, but for one particular request it doesn't work and exception is thrown:
com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $.results
The JSON I want to parse:
{
"id": 423,
"results": [
{
"id": "53484dfec3a3684b930000bd",
"iso_639_1": "en",
"iso_3166_1": "US",
"key": "u_jE7-6Uv7E",
"name": "Trailer",
"site": "YouTube",
"size": 360,
"type": "Trailer"
},
{
"id": "57e16bb0c3a36808bc000641",
"iso_639_1": "en",
"iso_3166_1": "US",
"key": "BFwGqLa_oAo",
"name": "Trailer",
"site": "YouTube",
"size": 1080,
"type": "Trailer"
}
]
}
The model classes:
public class VideosResponse {
private int id;
private List<Video> results;
//+ getters & setters
}
public class Video {
private String id;
#Json(name = "iso_639_1")
private String iso6391;
#Json(name = "iso_3166_1")
private String iso31661;
private String key;
private String name;
private String site;
private Integer size;
private String type;
//+getters & setters
}
This is Retrofit call:
#GET("3/movie/{id}/videos")
Call<List<Video>> movieVideos(
#Path("id") int id,
#Query("api_key") String apiKey);
So as you can see I'm expecting list of objects, but the JSON is actually an objecy itself, therefore I prepared my custom converter:
public class VideosJsonConverter {
#FromJson
public List<Video> fromJson(VideosResponse json) {
return json.getResults();
}
}
... and I'm adding it to my Retrofit like that:
public Retrofit provideRetrofit(#Named("baseUrl") String basUrl) {
Moshi moshi = new Moshi.Builder().add(new VideosJsonConverter()).build();
return new Retrofit.Builder()
.baseUrl(basUrl)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build();
}
My custom converter isn't actually called so it looks like Moshi can't convert JSON to my VideosResponse wrapper class. If I change my converter to accept Map<String, Object> it goes there, but not for VideosResponse. It also works when I change my retrofit enpoint to return directly VideosResponse. Is it possible that there is a conflict with other POJO classes (I have similar classes but with a list of different objects)?
The problem is that the adapter is going to be used by both your desired result and the inner list in VideosResponse. So, the adapter is expecting a VideoResponse-formatted JSON blob within the VideoResponse and fails when it finds the real array on reentry.
You can qualify one of the lists to differentiate them.
Here's an example of qualifying the resulting list.
#Retention(RUNTIME)
#JsonQualifier
public #interface Wrapped {
}
public class VideosJsonConverter {
#Wrapped #FromJson
public List<Video> fromJson(VideosResponse json) {
return json.results;
}
#ToJson
public VideosResponse toJson(#Wrapped List<Video> value) {
throw new UnsupportedOperationException();
}
}
#GET("3/movie/{id}/videos")
#Wrapped
Call<List<Video>> movieVideos(
#Path("id") int id,
#Query("api_key") String apiKey);