retrofit gson converter for nested json with different objects - java

I've JSON structure like follows -
{
"status": true,
"message": "Registration Complete.",
"data": {
"user": {
"username": "user88",
"email": "user#domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
}
Above format is common . Only data key can hold different types of information like user, product, invoice etc.
I want to keep status, message and data keys same in every rest response. data will be treated according to status and message will be displayed to user.
So basically, above format is desired in all apis. Only information inside data key will be different each time.
And I've setup a following class and set it up as gson converter - MyResponse.java
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private T data;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Deserializer.java
class Deserializer<T> implements JsonDeserializer<T>{
#Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException{
JsonElement content = je.getAsJsonObject();
// Deserialize it. You use a new instance of Gson to avoid infinite recursion to this deserializer
return new Gson().fromJson(content, type);
}
}
And used it as follows -
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(MyResponse.class, new Deserializer<MyResponse>());
...... ..... ....
restBuilder.setConverter(new GsonConverter(gsonBuilder.create()));
Service interface is as follows -
#POST("/register")
public void test1(#Body MeUser meUser, Callback<MyResponse<MeUser>> apiResponseCallback);
#POST("/other")
public void test2(Callback<MyResponse<Product>> apiResponseCallback);
Problem
I can access status and message fields from inside callback. But information inside data key is not parsed and model like MeUser and Product always returns as empty.
If I change json structure to following above code works perfectly -
{
"status": true,
"message": "Registration Complete.",
"data": {
"username": "user88",
"email": "user#domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
How can I have it worked with specifying separate key inside data object and parse it successfully ?

If I can suggest to change something in json is that you have to add at one new field that defines the type of data, so json should look like below:
{
"status": true,
"message": "Registration Complete.",
"dataType" : "user",
"data": {
"username": "user88",
"email": "user#domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
The MyResponse class has to have new filed DataType so it should look like below:
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private DataType dataType ;
private T data;
public DataType getDataType() {
return dataType;
}
//... other getters and setters
}
The DataType is an enum which defines type of data. You have to pass Data.class as param in constructor. For all data types you have to create new classes. DataType enum should look like below:
public enum DataType {
#SerializedName("user")
USER(MeUser.class),
#SerializedName("product")
Product(Product.class),
//other types in the same way, the important think is that
//the SerializedName value should be the same as dataType value from json
;
Type type;
DataType(Type type) {
this.type = type;
}
public Type getType(){
return type;
}
}
The desarializator for Json should looks like below:
public class DeserializerJson implements JsonDeserializer<MyResponse> {
#Override
public MyResponse deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonObject content = je.getAsJsonObject();
MyResponse message = new Gson().fromJson(je, type);
JsonElement data = content.get("data");
message.setData(new Gson().fromJson(data, message.getDataType().getType()));
return message;
}
}
And when you create RestAdapter, in the line where you register Deserializator, you should use this :
.registerTypeAdapter(MyResponse.class, new DeserializerJson())
Other classes (types of data) you define like standard POJO for Gson in separated classes.

Your issue is because the data attribute is defined as T which you expect to be of types MeUser, Product, etc, but is actually of an object which has inner attribute like user. To resolve this, you need to introduce another level of classes which has the required attributes user, product, invoice etc. This can be easily achieved using static inner classes.
public class MeUser{
private User user;
public static class User{
private String username;
//add other attributes of the User class
}
}

Might be a little bit off-topic, but what happens if the inner object contains a Date property? My TypeAdapter looks like this:
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(GenericNotificationResponse.class, new NotificationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
But due to the parsing which is done by this : message.setData(new Gson().fromJson(data, message.getDataType().getType()));
It will throw an error whenever it will try to deserialize the Date property. Is there a quick fix for this?
EDIT: Marked the answer as accepted, definitely :) it helped me fix my issue. But now there's this problem with the date deserializer.

Related

how to iterate through this json object and format it

I have multiple JSON files, but they all look similar to this one (some much longer):
{
"$id": "http://example.com/myURI.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"description": "Sample Procedure schema.",
"properties": {
"prop1": {
"description": "",
"type": "string"
},
"prop2": {
"description": "",
"type": "number"
}
"prop3": {
"description": "",
"type": "string"
}
}
}
I want to extract the names (ex. "prop1","prop2") along with their types (ex. "string,"number") and format it to look something like the following:
public static class Whatever {
#JsonProperty
public String prop1;
#JsonProperty
public Integer prop2;
#JsonProperty
public String prop3;
public Whatever() {}
public Whatever(String prop1, Integer prop2, String prop3){
this(prop1, prop2, prop3);
}
}
I've never used Java before so I'm not sure where to begin in creating this script. Mainly I'm concerned with how I will iterate through the json object.
I would Approach this this way:
Create a main class that holds all the information of the JSON and a Secondary class that keeps the information of the prop properties of the JSON:
public class MainJsonObject
{
private int $id;
private int $schema;
// All the other objects that you find relevant...
private List<SecondaryJsonObject> propsList = new ArrayList<>(); // This will keep all your other props with description and type
// Getter and setter, do not forget them as they are needed by the JSON library!!
}
public class SecondaryJsonObject
{
private String description;
private String type;
// Getter and setter, do not forget them !!
}
You could iterate through your JSON Object this way:
First of all include the JSON Library in your project.
Then iterate through your JSON like this:
JSONObject jsonObject = new JSONObject(jsonString);
MainJsonObject mjo = new MainJsonObject();
mjo.set$id(jsonObject.getInt("$id")); // Do this for all other "normal" attributes
// Then we start with your properties array and iterate through it this way:
JSONArray jsonArrayWithProps = jsonObject.getJSONArray("properties");
for(int i = 0 ; i < jsonArrayWithProps.length(); i++)
{
JSONObject propJsonObject = jsonArrayWithProps.getJSONObject(i); // We get the prop object
SecondaryJsonObject sjo = new SecondaryJsonObject();
sjo.setDescription(propJsonObject.getString("description"));
sjo.setTyoe(propJsonObject.getString("type"));
mjo.getPropsList().add(sjo); // We fill our main object's list.
}

Suggestion(s) for deserializing Json objects which has some attributes with a dynamic type with gson

I want to deserialize the following object:
{
"qid": "226",
"parent_qid": "225",
"sid": "298255",
"gid": "25",
"type": "M",
"title": "SQ001",
"question": "question text",
"preg": null,
"help": null,
"other": "N",
"mandatory": null,
"question_order": "1",
"language": "de",
"scale_id": "0",
"same_default": "0",
"relevance": "1",
"modulename": "",
"available_answers": "No available answers",
"subquestions": "No available answers",
"attributes": "No available attributes",
"attributes_lang": "No available attributes",
"answeroptions": "No available answer options",
"defaultvalue": null
}
Members with types:
private HashMap<String, String> available_answers;
private HashMap<Integer, SubQuestion> subquestions;
My problem is that in some cases the attributes "answeroptions" and "available_answers" ( perhaps one or two more) aren't just strings but other objects.
A) I read about creating a custom deserializer to handle such cases but this would lead to the case that I also would have to deserialize the other attributes with static types ( more unnecessary code ).
B) Another approach which came to my mind is that I could "deserialize" the attributes to JsonObjects and deserialize them completely in the corresponding getters.
So my question is if you could suggest me approach a), b) or another one.
Greetings and thanks in advance!
If these fields only have a string JSON value if they have no value, then you could use the #JsonAdapter annotation and a custom TypeAdapterFactory:
/**
* Only for use on fields annotated with {#code #JsonAdapter}.
*/
public class StringAsNullTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
TypeAdapter<T> delegate = gson.getAdapter(type);
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, Object value) throws IOException {
// You will have to implement this in case you are
// also writing JSON data
throw new UnsupportedOperationException();
}
#Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.STRING) {
in.skipValue(); // skip the string
return null;
}
return delegate.read(in);
}
};
}
}
You can then use that TypeAdapterFactory like this:
class Quiz {
public String qid;
public String parent_qid;
// ...
#JsonAdapter(StringAsNullTypeAdapterFactory.class)
public HashMap<String, String> available_answers;
#JsonAdapter(StringAsNullTypeAdapterFactory.class)
public HashMap<Integer, SubQuestion> subquestions;
}
You then use your Gson instance as usual, there is no further configuration required.

Create JSON object in Java8 using com.google.code.gson

I am trying to create a JSON Object using com.google.code.jso in JAVA 8. I have a list of objects and i am iterating through them. The object is as:
public class MyObject {
private String name, status, cause, id;
public String getName() {
return name;
}
public String getStatus() {
return status;
}
public String getCause() {
return cause;
}
public String getId() {
return id;
}
}
I have a list of above objects and i am trying to convert them to JSON using the below (the relevant code):
import com.google.gson.JsonObject;
JsonObject status = new JsonObject();
for (MyObject obj : objectLists){
status.add(obj.getId(),
new JsonObject()
.add("Name: ", obj.getName())
.add("Status: ", obj.getStatus())
.add("Cause: ", obj.getCause())
);
}
I was hoping to get a JSON of the following form:
{
"0 (this is the id i get from myObject.getId())": {
"name": The name i get from myObject.getName(),
"Status": The status from myobject.getStatus(),
"cause": The status from myobject.getCause()
},
"1": {
"name": "myname",
"Status": "mystatus",
"cause": "cause"
}
}
So I have 2 question.
I am getting an error in creating the Json object. Wrong 2nd
argument type. Found: 'java.lang.String', required:
'com.google.gson.JsonElement' I understand that i have to change
the second parameter but i could not find in the docs how to do
that.
How can i pretty print this.
Thanks
You used wrong method:
JsonObject.addProperty(), not JsonObject.add()
JsonObject.add(String, JsonElement) - adds nested json element to your object:
{
"root": {
"nested": {}
}
}
JsonObject.addProperty(String, String) - adds property to your object (it may be any primitive or String):
{
"root": {
"property": "some string"
}
}
Why not using this instead.
Gson.toJson(obj);
You should wrap your strings in JsonPrimitive objects, for example:
new JsonObject().add("Name: ", new JsonPrimitive(obj.getName()));
To enable pretty printing, you have to use GsonBuilder.setPrettyPrinting():
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(obj));

Gson json getting the result

I have a json string like this:
{
"d": {
"results": [
{
"__metadata": {
"uri": "http://localhost:2000",
"key_fields": "Accountnum",
"rows_affected": 0,
"last_autoinc": 0
},
"Accountnum": "9999999",
"workphone": null,
"name": "Smith",
"address": "33 Main St",
"city": "Anytown",
"state": "FL",
"zip": "33333",
}
]
}
}
and I tried to deserialize it according to diffrent questions here on stackoverflow, but I can't get it right.
Here is what I did I created a class, I only need the accountnum and name.
public class Result {
#SerializedName("Accountnum")
public String accountnumStr;
#SerializedName("name")
public String nameStr;
}
I have a string with the json myresult.
Gson gson = new Gson();
Result result = gson.fromJson(myresult,Result.class);
myName.setText(result.nameStr);
I receive an empty string.
Thanks
Since there is a object holding the result object your trying to create, you have make the result class an inner class. You're Result class would have to look like this:
public class ResultParent {
public class Result {
#SerializedName("Accountnum")
public String accountnumStr;
#SerializedName("name")
public String nameStr;
}
}
If you visualize your JSON in terms of class objects then it will be no problem at all. Imagine that your root object contains a property named "d" of type "D". And type "D" contains a list of "results" of another type "Result". The result contains its properties as above. Here's the picture of it:
class RootObj {
D d;
}
class D {
List<Result> results;
}
class Result {
#SerializedName("Accountnum")
public String accountnumStr;
#SerializedName("name")
public String nameStr;
}
To get the objects from a JSON, you would do the following:
Gson g = new Gson();
RootObj ro = g.fromJson(jsonString, RootObj.class);

Help me to parse this kind of json for android

I have this type of json from api:
[
{
"id": "12",
"name": "elo2",
"status": "ok",
"vulumes": [
{
"id": 17592,
"name": "vol1",
"status": "ok"
}
]
},
{
"id": "13",
"name": "elo",
"status": "ok",
"vulumes": [
{
"id": "17596",
"name": "vol2",
"status": "ok"
}
]
}
]
ID 12 and 13 its shelf, volumes is a part of shelf with id 17592 and 17596
I cannot to parse it with json, i trying to use gson or json, but i can't understand how to get sime block, for e.g. block with id 12 for parse info about shelf and existing volumes at this shelf.
Can you help me? In other apis i can see named objects vith k/v, but there is nothing.
With Gson, here's one simple approach you could take. This demonstrates a Java data structure that matches the JSON example, along with how to deserialize and serialize it using Gson.
public class Foo
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
Type thingsType = new TypeToken<List<Thing>>() {}.getType();
List<Thing> things = gson.fromJson(new FileReader("input.json"), thingsType);
System.out.println(gson.toJson(things));
}
}
class Thing
{
String id;
String name;
String status;
List<Vulume> vulumes;
}
class Vulume
{
String id;
String name;
String status;
}
Since the attributes of the two object types are almost identical, then maybe the intention was that there be only one object type, with a reference to a collection of objects of the same type. Here's what that would look like.
public class Foo
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
Type thingsType = new TypeToken<List<Vulume>>() {}.getType();
List<Vulume> things = gson.fromJson(new FileReader("input.json"), thingsType);
System.out.println(gson.toJson(things));
}
}
class Vulume
{
String id;
String name;
String status;
List<Vulume> vulumes;
}

Categories