Problem
I consume an API which gives me this type of JSON:
{
"data": {
"name": "Start",
"pid": "1",
"position": {
"data": {
"x": "31",
"y": "330"
},
"metadata": "empty"
}
},
"metadata": "empty"
}
I have created the classes with objects with the same structure as the above JSON. I use the retrofit
lib in Android which inside uses GSON
for parsing the JSON.
My model classes would be like this:
MResponse.class
public class MResponse {
@SerializedName("data")
public User user;
String metadata;
}
User.class
public class User {
public String name;
public String pid;
@SerializedName("position")
public PositionData positionData;
}
PositionData.class
public class PositionData {
@SerializedName("data")
public Position position;
public String metadata;
}
Position.class
public class Position {
public String x;
public String y;
}
Now this works fine for me. But as you can see for every model I have to create a parent which will have the same structure just changes the child. This fact doubles the classes that I use for my models. I would like to ask if there is a better way to avoid all these classes.
I don’t want to use inner classes. I was thinking that the guys that have done the JSON like this must have had a reason why they did it like this and also a way to make the parsing more easier.
Usually I was used to parse this kind of JSON structure:
{
"data": {
"name": "Start",
"pid": "1",
"position": {
"x": "31",
"y": "330"
}
}
}
And here it’s easier if I would follow the solution above.
EDIT
Also any solution in Kotlin is welcomed
Solution
You can create a generic container/entity for “data” and “metadata” and reuse it. e.g.:
data class MEntity<T>(
var data: T? = null,
var metadata: String? = null
)
data class User(
var name: String? = null,
var pid: String? = null,
var position: MEntity<Position>? = null
)
data class Position(
var x: String? = null,
var y: String? = null
)
Due to type erasure you must use a TypeToken
to deserialize the JSON into a generic type:
gson.fromJson<MEntity<User>>(json, object : TypeToken<MEntity<User>>() {}.type)
This isn’t very convenient but thankfully Kotlin allows us to use reified type parameters to define an extension function to simplify this:
inline fun <reified T> Gson.fromJsonToGeneric(json: String): T {
return fromJson(json, object : TypeToken<T>() {}.type)
}
Now the usage becomes much simpler:
val userMEntity = gson.fromJsonToGeneric<MEntity<User>>(json)
println(userMEntity)
Output:
MEntity(data=User(name=Start, pid=1, position=MEntity(data=Position(x=31, y=330), metadata=empty)), metadata=empty)
Well the solution that I was asking for was quite simple in my opinion, I just didn’t know it in the beginning. I found this times ago but now I want to write it here. I haven’t used the Kotlin
solution mentioned from @mfulton
but I think that it’s also the right answer.
Based on source code of Retrofit
and some other related answers about Java generics
this was the answer for my problem:
public class DataResponse<T, R> {
public T data;
@SerializedName("meta")
public R metadata;
}
and this is an example how I can use it:
Observable<DataResponse<User, BaseMeta>> getUser()
For me this was the solution. If there are better solution out there, I am ready to accept them.