Related
I'm trying to fetch JSON data from my website throw REST API with retrofit2.
But when I run the app this error message show:
Can not find a (Map) Key deserializer for type [simple type, class com.example.app.ReferralApiModel]
I'm using retrofit library.
This is my code for the retrofit call:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConfig.URL)
.addConverterFactory(JacksonConverterFactory.create())
.client(defaultHttpClient)
.build();
ReferralsPlaceHolderApi placeHolderApi = retrofit.create(ReferralsPlaceHolderApi.class);
Call<List<Map<ReferralApiModel, String>>> call = placeHolderApi.getReferrals();
And this is my ReferralsPlaceHolderApi class:
public interface ReferralsPlaceHolderApi {
#JsonDeserialize(keyAs = ReferralsCustomDeserializer.class)
#GET(AppConfig.ENDPOINT_REFERRALS)
Call<List<Map<ReferralApiModel, String>>> getReferrals();
}
Also this is my ReferralApiModel class:
public class ReferralApiModel {
private String date;
private String amount;
private String currency;
private String status;
public ReferralApiModel() {}
public ReferralApiModel(String date, String amount, String currency, String status) {
this.date = date;
this.amount = amount;
this.currency = currency;
this.status = status;
}
public String getDate() {
return date;
}
public String getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
public String getStatus() {
return status;
}
}
This is the json data that I'm trying to get:
"[{\"id\":\"1\",\"refferal_wp_uid\":\"0\",\"campaign\":\"\",\"affiliate_id\":\"5\",\"visit_id\":\"1\",\"description\":\"\",\"source\":\"woo\",\"reference\":\"302\",\"reference_details\":\"68\",\"parent_referral_id\":\"0\",\"child_referral_id\":\"0\",\"amount\":\"1500.00\",\"currency\":\"\د\ج\",\"date\":\"2022-01-31 12:53:29\",\"status\":\"0\",\"payment\":\"0\",\"username\":\"aaa\"},{\"id\":\"2\",\"refferal_wp_uid\":\"0\",\"campaign\":\"\",\"affiliate_id\":\"5\",\"visit_id\":\"2\",\"description\":\"\",\"source\":\"woo\",\"reference\":\"303\",\"reference_details\":\"68\",\"parent_referral_id\":\"0\",\"child_referral_id\":\"0\",\"amount\":\"1500.00\",\"currency\":\"\د\ج\",\"date\":\"2022-01-31 13:03:43\",\"status\":\"1\",\"payment\":\"0\",\"username\":\"aaa\"},{\"id\":\"3\",\"refferal_wp_uid\":\"0\",\"campaign\":\"\",\"affiliate_id\":\"5\",\"visit_id\":\"2\",\"description\":\"\",\"source\":\"woo\",\"reference\":\"304\",\"reference_details\":\"68\",\"parent_referral_id\":\"0\",\"child_referral_id\":\"0\",\"amount\":\"1500.00\",\"currency\":\"\د\ج\",\"date\":\"2022-01-31 13:04:33\",\"status\":\"2\",\"payment\":\"0\",\"username\":\"aaa\"}]"
Can anyone help me with this?.
Also I've found that this problem may be a class mapping problem, from this answer :
https://stackoverflow.com/a/16383752/8055951
If it's ?!, Can someone tell me how to map the ReferralsPlaceHolderApi class.
Thanks.
Jackson cannot deserialize custom classes as map keys. The key of your deserialized map is ReferralApiModel. I order to achieve it, you need to write your own KeyDeserializer and register it for your class with Jackson. You can see here or here how to do that.
Also the json string in the question makes it look as if you don't need to deserialize into List<Map<ReferralApiModel, String>>, but into List<ReferralApiModel> instead. Which would make writing custom key deseriaslizers redundant.
Edit: Ok, receiving json array, which has been json sting-ified is just strange. It would be best, if someone on your team is responsible for this API and can fix it. If not, you have workarounds:
Parse twice with object mapper - first parse it to normal string, which would be json array, then parse this string into List<YourObject>
ObjectMapper mapper = new ObjectMapper();
String string = mapper.readValue(initialJson, String.class);
List<ReferralApiModel> list = mapper.readValue(string, TypeFactory.defaultInstance().constructCollectionType(List.class, ReferralApiModel.class));
list.forEach(System.out::println);
Turn it manually into proper json array. That means remove first and last char - double quote, and remove all those escapes - \. Something like this:
String jsonString = "the string";
jsonString = jsonString.substring(1, jsonString.length() - 1).replace("\\", "");
ObjectMapper mapper = new ObjectMapper();
List<ReferralApiModel> list = mapper.readValue(jsonString, TypeFactory.defaultInstance().constructCollectionType(List.class, ReferralApiModel.class));
list.forEach(System.out::println);
I have this as a substring. It is a JSON string. I am trying to get the id string from it. I was able to do this by using two indexOf's and then substring the two indexOf's. What is a better solution.
Here is my string
"{"id":"762c094a-4b65-499e-b5b2-de34ef8d726e","createdTimestamp":1605558195131,"username":"sssdv","enabled":false,"totp":false,"emailVerified":false,"firstName":"cdf","lastName":"dddz","email":"hgddf#fdaddf.com","disableableCredentialTypes":[],"requiredActions":[],"notBefore":0,"access":{"manageGroupMembership":true,"view":true,"mapRoles":true,"impersonate":true,"manage":true}}"
And here is my code.
int id = results.indexOf("id");
int cr = results.indexOf("createdTimestamp");
String strId = results.substring(id + 5, cr - 3);
A better solution is to use an actual JSON parser. There are plenty out there. Take a look at this answer on a different question. I would suggest using Gson:
String json = "{\"id\":\"762c094a-4b65-499e-b5b2-de34ef8d726e\",\"createdTimestamp\":1605558195131,\"username\":\"sssdv\",\"enabled\":false,\"totp\":false,\"emailVerified\":false,\"firstName\":\"cdf\",\"lastName\":\"dddz\",\"email\":\"hgddf#fdaddf.com\",\"disableableCredentialTypes\":[],\"requiredActions\":[],\"notBefore\":0,\"access\":{\"manageGroupMembership\":true,\"view\":true,\"mapRoles\":true,\"impersonate\":true,\"manage\":true}}";
Gson gson = new GsonBuilder().setPrettyPrinting().create(); // Create the Gson instance
JsonElement element = gson.fromJson(json, JsonElement.class); // Parse it
String id = element.getAsJsonObject().get("id").getAsString(); // Get your desired element
System.out.println(id);
An even better solution would be to create a class with the fields from your JSON and parse the JSON string to that class:
public class MyObject {
// The names and types of these fields must match the ones in your JSON string
private String id, username, firstName, lastName, email;
private long createdTimestamp;
private boolean enabled, totp, emailVerified;
private String[] disableableCredentialTypes, requiredActions;
private int notBefore;
private Access access;
public String getId() {
return id;
}
// Other getters and setters...
private static class Access {
private boolean manageGroupMembership, view, mapRoles, impersonate, manage;
// ...
}
public static void main(String[] args) throws IOException {
String json = "{\"id\":\"762c094a-4b65-499e-b5b2-de34ef8d726e\",\"createdTimestamp\":1605558195131,\"username\":\"sssdv\",\"enabled\":false,\"totp\":false,\"emailVerified\":false,\"firstName\":\"cdf\",\"lastName\":\"dddz\",\"email\":\"hgddf#fdaddf.com\",\"disableableCredentialTypes\":[],\"requiredActions\":[],\"notBefore\":0,\"access\":{\"manageGroupMembership\":true,\"view\":true,\"mapRoles\":true,\"impersonate\":true,\"manage\":true}}";
Gson gson = new GsonBuilder().setPrettyPrinting().create(); // Create the Gson instance
MyObject object = gson.fromJson(json, MyObject.class); // Parse the string to your data type
System.out.println(object.getId()); // Print the id
}
}
String results = "{\"id\":\"762c094a-4b65-499e-b5b2-de34ef8d726e\",\"createdTimestamp\":1605558195131,\"username\":\"sssdv\",\"enabled\":false,\"totp\":false,\"emailVerified\":false,\"firstName\":\"cdf\",\"lastName\":\"dddz\",\"email\":\"hgddf#fdaddf.com\",\"disableableCredentialTypes\":[],\"requiredActions\":[],\"notBefore\":0,\"access\":{\"manageGroupMembership\":true,\"view\":true,\"mapRoles\":true,\"impersonate\":true,\"manage\":true}}";
String[] parts = results.split("\"");
System.out.println(parts[3]); //gives the id, every time
I am using Jackson library with java 11 so basically I am able to read the below JSON into a string format
{
"schemas":[
"urn:params:core:2.0:User",
"urn:params:core:3.0:User"
],
},
}
here below is the set in which I have to fill the values of schemas from above json
private Set<String> setschemas = null;
right now I am able to read the above json into a string named finaljson , now please advise how can I read the differnt value of schemas from above json string named finaljson and set it to set named setschemas
if (node.has("schemas")) {
// *** here I want to read the differernt value of schemas and set it to a set
// named setschemas
// *****
}
you can create the following classes that represent the json structure
class MyJsonObject {
private AppIdentity appIdentity;
private Set<String> schemas;
private String userName;
}
class AppIdentity {
private String clientId;
private String username;
}
than you can use
final MyJsonObject myJsonObject = new ObjectMapper().readValue(finaljson, MyJsonObject.class); to read the json to JAVA object
so it can manipulated like myJsonObject.schemas.size() > 0 and such...
there are a lot of examples in the internet
*keep in mind, this solution only works when the json structure and fields name are known in advanced
With your approach, this would be simplest one:
if(node.has("schemas")) {
JsonNode schemaNode = node.get("schemas");
Set<String> schemaSet = objectMapper.convertValue(schemaNode, Set.class);
System.out.println("schemaSet" + schemaSet);
}
There are various ways to deal with JSON one is described here
1) You can create a class of JSON structure as follows with help online JSON to POJO convertor (Note:: Add Setters and Getters with help of IDE)
class AppJson {
private Set<AppIdentity> appIdentity;
private Set<String> schemas;
private String userName;
private Manager ManagerObject;
private String division;
private String organization;
private String costCenter;
private String employeeNumber;
}
class AppIdentity {
private String clientId;
private String username;
}
class Manager {
private String value;
private String $ref;
private String displayName;
private String $Ref;
}
2) Use above for object conversion.
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"appIdentity\":[{\"clientId\":\"9a41763c642\",\"username\":\"XXX\"}],\"schemas\":[\"urn:params:core:2.0:User\",\"urn:params:core:3.0:User\"],\"userName\":\"ajklmnop_699100\",\"manager\":{\"value\":\"string\",\"$ref\":\"sdkoirk\",\"displayName\":\"string\",\"$Ref\":\"sdkweoirk\"},\"division\":\"string\",\"organization\":\"string\",\"costCenter\":\"string\",\"employeeNumber\":\"string\"}\n"
+ "";
AppJson appJson = objectMapper.readValue(jsonString, AppJson.class);
System.out.println("json " + appJson.getSchemas());
Here you will get the schemas.
I am using to Retrofit to handle Calls to my API for an Android Application. I am trying to get Retrofit to handle the parsing of the JSON, and creating a list of Objects in accordance with the POJO i have created.
The error i receive is "com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 176".
I used JsonSchema2Pojo to generate my java classes. The classes and associated JSON are as follows.
{"status":"success","data":[{"sort_key":1,"event_id":1947357,"title":"2014 US Open Tennis Session 15 (Mens\/Womens Round of 16)","datetime_utc":"2014-09-01T15:00:00","venue":{"city":"Flushing","name":"Louis Armstrong Stadium","extended_address":"Flushing, NY 11368","url":"https:\/\/seatgeek.com\/venues\/louis-armstrong-stadium\/tickets\/?aid=10918","country":"US","display_location":"Flushing, NY","links":[],"slug":"louis-armstrong-stadium","state":"NY","score":0.73523,"postal_code":"11368","location":{"lat":40.7636,"lon":-73.83},"address":"1 Flushing Meadows Corona Park Road","timezone":"America\/New_York","id":2979},"images":["https:\/\/chairnerd.global.ssl.fastly.net\/images\/performers-landscape\/us-open-tennis-45e2d9\/5702\/huge.jpg","https:\/\/chairnerd.global.ssl.fastly.net\/images\/performers\/5702\/us-open-tennis-c1ccf7\/medium.jpg","https:\/\/chairnerd.global.ssl.fastly.net\/images\/performers\/5702\/us-open-tennis-01f513\/large.jpg","https:\/\/chairnerd.global.ssl.fastly.net\/images\/performers\/5702\/us-open-tennis-4e07f2\/small.jpg"]}
From this i believe i need to generate 3 POJO's, my higher level "EventObject" Class, A Location Class, and a Venue Class. These classes and their variables follow:
EventObject Class:
public class EventObject {
private Integer sortKey;
private Integer eventId;
private String title;
private String datetimeUtc;
private Venue venue;
private List<String> images = new ArrayList<String>();
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
Location Class:
public class Location {
private Float lat;
private Float lon;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
Venue Class:
public class Venue {
private String city;
private String name;
private String extendedAddress;
private String url;
private String country;
private String displayLocation;
private List<Object> links = new ArrayList<Object>();
private String slug;
private String state;
private Float score;
private String postalCode;
private Location location;
private String address;
private String timezone;
private Integer id;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
My interface for the Api Call is as follows:
public interface UserEvents {
#GET("/user/get_events")
void getEvents(#Header("Authorization")String token_id,
#Query("event_type")String event_type,
#Query("postal_code")int postalCode,
#Query("per_page") int perPage ,
#Query("lat") int lat,
#Query("lon") int lon,
#Query("month")int month,
#Query("page")int page,
Callback<List<EventObject>>callback) ;
}
Here is its implementation in my code :
UserEvents mUserEvents = mRestAdapter.create(UserEvents.class);
mUserEvents.getEvents(token_Id, "sports",11209,25,0, 0, 9, 2, new Callback <List<EventObject>>() {
#Override
public void success(List<EventObject> eventObjects, retrofit.client.Response response) {
Log.d(TAG,"Success");
}
There is alot going on here, but i believe that i am probably going wrong with how i am handling the JSON. When i copied and pasted in my JSON to the Pojo generator, i did not include "status:success, " data:{
I essentially just used the entire entry of an element in the Array ( everything from {sort_key onward until the next sort key ) and pushed that through the converter.
This is my first try at Retrofit and API work, and parsing anything this complicated.
I am hoping its something that someone else will be able to point out. I have googled as well i could to sort this out with no luck.
Thanks for looking.
The main problem is that you are not getting the root element of the response. You need to create an entity "response" that gets the items status and data. It would look something like this:
public class RootObject {
#Expose
private String status;
#Expose
private EventObject data;
//getters and setters here
}
Then when you make the callback you should point to your RootObject, mUserEvents.getEvents(token_Id, "sports",11209,25,0, 0, 9, 2, new Callback <RootObject>()
One more thing, Retrofit uses GSON to parse your json reponse. It means that when you create the entities, the variables need to match the name of the objects coming in the response. If it doesn't you need to tell GSON how to map the variables, like this:
#SerializedName("extended_address")
#Expose
private String extendedAddress;
In that case the value coming in the json is "extended_address" and will be mapped to the String extendedAddress. If you don't put that #SerializedName line the parsing will fail. If you want to skip that line then you can call your variable "extended_address" so it matches the response.
The #Expose is needed by GSON to parse the variable below it. If a variable doesn't have it then GSON will ignore that parsing. So you need to fix both the #Expose and #SerializedName on your entities so GSON works correctly.
Hope it helps.
I'm having following class
public class ReturnData {
public ReturnData() {
OperationResult = Result.Failed;
Messages = "An Error Occured";
UpdateAvailable = "0";
ResultData = "";
}
public Result OperationResult;
public String Messages;
public String UpdateAvailable;
public Object ResultData;
}
I'm having json string like,
{"OperationResult":0,"Messages":"","UpdateAvailable":"","ResultData":{"SessionId":"3b44a524-fc2a-499b-a16e-6d96339a6b5b","UserName":"admin","AccoundId":null,"Roles":["Administrator"],"DisplayName":"Admin","Status":3,"Type":1}}
I want to assign this json string to above class.I'm using GSON for assign json string to java object.In normal class i can assign json string to java object. But for this class i couldn't assign directly. Please any one help me,
Now i'm assigning like,
String formatedjsonstring = {json string};
Log.i("FORMAT STRING:",formatedjsonstring);
Gson gson = new Gson();
ReturnData returndata = (ReturnData) gson.fromJson(
formatedjsonstring, ReturnData.class);
You could use JavaJson from sourceforge. You could pass your json string to JsonObject .parse().
Try this
JsonObject json = JsonObject .parse("{\"OperationResult\":0, \"Messages\":\"UpdateAvailable\"");
System.out.println("OperationResult=" + json.get("OperationResult"));
System.out.println("Messages=" + json.get("Messages"));
https://sourceforge.net/projects/javajson/
Since your Java class doesn't resemble your JSON in any way, shape or form ... you're going to have a problem with that.
Problem #1: OperationResult should be an int
Problem #2: You're declared ResultData as an Object ... Java doesn't work like that.
You need your POJO to match the JSON:
public class ReturnData {
public ReturnData() {
OperationResult = Result.Failed;
Messages = "An Error Occured";
UpdateAvailable = "0";
ResultData = "";
}
public int OperationResult;
public String Messages;
public String UpdateAvailable;
public MyResultData ResultData;
}
class MyResultData {
public String SessionId;
public String UserName;
public String AccountId;
public List<String> Roles;
public String DisplayName;
public int Status;
public int Type;
}
ReturnData rd = new Gson().fromJson(jsonString, ReturnData.class);
I'd also consider using Gson's #SerializedName("name") annotation to convert the PascalCase field names in your JSON to camelCase field names in Java.
#SerializedName("OperationResult") public int operationResult;
Try this:
java.lang.reflect.Type type = new TypeToken<ReturnData>(){}.getType();
ReturnData rd = new Gson().fromJson(jsonString, type);