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();
Related
I have got two main model classes: Customer and Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
I want to build json file with Map<Customer, List<Product>>
When I write to json file data with my method which works correct - I am sure about this - the json file shows this syntax
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
Then when i want to read this file, the error Exception in thread "main" java.util.NoSuchElementException: No value present occurs so I think that the Customer syntax from json file is not recognized.
So I tried to write data to json file on my own with this syntax below, but it does not work
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json converter method:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
#Tom is right on the issues you've faced with. I'll explain why and suggest one more solution.
Your first JSON is technically a valid JSON but it cannot be deserialized, because the map keys are results of the Customer.toString() method Gson uses by default. This is why it looks weird, acts like a debug string, and can't be deserialized back: there it is almost always no way to restore an object from the toString() result (toString is designed mostly for debugging/logging purposes providing basic information regarding the state of a particular object that does not need to expose its all internals at all).
Your second JSON is invalid JSON. Period.
Tom's suggestion of making the list of products a part of the customer class is totally fine. Having it implemented like that lets you to serialize everything as a list like this:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
Hint: separating domain objects (Customer and Product) and representation objects for data transfer (CustomerDto and ProductDto) is usually a fine idea too since it allows to create representation for any concrete representation implementation (one for various JSON implementation libraries, two for other-format-oriented tools, third for persistence, four for UI views, etc), so it might be implemented like converting Map<Customer, List<Product>> to List<CustomerDto> and back (possibly by using mapper-generators like MapStruct).
If for whatever reason it is not possible to reorganize your domain classes or create Gson-friendly DTO-mappings, or you're fine to keep it as simple as possible and you're fine with having not that trivial JSON structure (as long as you understand implications of the format in this solution: evolution, distribution, etc), then you can enable special Gson mode to support this kind of maps. It generates valid JSONs that can be serialized and deserialized back, but the way it is implemented looks a bit of anti-pattern to me because of losing semantics due to using arrays as the data container.
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Customer {
final String name;
}
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
#Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
Note that it generates JSON like this (index 0 means the key, index 1 means the value):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]
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 have been looking solution for this problem but could not find one so asking this question.
I have some data which looks like this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation": {
"firstName": "jio",
"lastName": "g",
"mobileNumber": "1234567890",
},
"accountBalance": 0,
}
]
},
I want to write a java code to change the data structure to this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation_firstName":"jio",
"personalInformation_lastNAme":"g",
"personalInformation_mobileNumber":"1234567890",
"accountBalance": 0,
}
]
},
I am getting data from db as:
#Override
public List<User> getAllUsers() {
logger.debug("entering all users method");
List<User> allUsers=mongoOperations.findAll(User.class);
for (User user : allUsers) {
PersonalInformation info=user.getPersonalInformation());
//manipulation code here
user.setPersonalInformation(info);
}
return allUsers;
}
So I want to write a logic so that i can convert the data in desired format and send it a return type. I know how to do same thing using J query but I want to do it in backend so any code for the above or any link will help.
I have fond one solution which is very simple.So, basically when we create object for nested data we create it like this in JAVA.
public MyClass{
public String name;
public String contact;
public PersonalInformation personalinformation;
//setters and getter here
}
this will give me data as
"MyClass":{
"name": "abc",
"contact": "12345",
"personalInformation":{
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
}
so to remove this nested data we need to use #JsonUnwrapped which removes all the nested object and add it to our main object.
public MyClass{
public String name;
public String contact;
#JsonUnwrapped
public PersonalInformation personalinformation;
//setters and getter here
}
which will change the data structure as:
"MyClass":{
"name": "abc",
"contact": "12345",
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
for more reference you can check this link http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Hope this helps.
There are multiple possible solutions. As Prabhav has mentioned the most intuitive one would be to create a new class and from there a object which can be transformed with a library to a JSON.
Variant one:
The new class would look like your data structure you want and access would be:
PersonalInformationJson pf = new PersonalInformationJson();
pf.setFirstName = info.getPersonalInformation_firstName
//... setting the rest of the object
//using jackson
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
String jsonString = mapper.writeValueAsString(u);
}
The other easier version to create a string, either per hand or use a lib:
// using org.json.JSONObject
String jsonString = new JSONObject().put("personalInformation_firstName", info.value())
.put("personalInformation_lastNAme", info.value());
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"]}]
I have a json like this:
{
"games": [
{
"id": "mhhlhlmlezgwniokgawxloi7mi",
"from": "425364_456#localhost",
"to": "788295_456#localhost",
"token": "xqastwxo5zghlgjcapmq5tirae",
"desc": "6CeF9/YEFAiUPgLaohbWt9pC7rt9PJlKE6TG6NkA4hE=",
"timestamp": 1412806372232
},
{
"id": "62jzlm64zjghna723grfyb6y64",
"from": "425364_456#localhost",
"to": "788295_456#localhost",
"token": "xqastwxo5zghlgjcapmq5tirae",
"desc": "Z/ww2XroGoIG5hrgiWsU1P8YHrv4SxiYHHoojzt9tdc=",
"timestamp": 1412806373651
}
]
}
I'm trying to deserialize it to an Object with ObjectMapper. Essentially as you can see, it is a List of games.
I have classes like these:
#JsonRootName(value="games")
public class GameJson{
private List<Game> games;
// getters and setters
}
the Game class is here:
public class Game{
private String id;
private String from;
private String to;
private String token;
private String desc;
private Instant timestamp;
// getters and setters
}
In my code, the ObjectMapper is doing this:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
GameJson json = mapper.readValue(
new FileInputStream(gamesFile), GameJson.class);
Then I get this error:
Can not deserialize instance of com.games.collection.GameJson out of START_ARRAY token
I am trying different ways to do this, but coming out with no luck. Can someone please help?
Thanks!
Get rid of
#JsonRootName(value="games")
That annotation identifies the annotated type as the target for the JSON object mapped to a JSON key named "games". In your case, that is a JSON array. An array cannot be deserialized into your GameJson class.
As you stated in the comments, you also need to remove the configuration that enables #JsonRootName.
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);