Java/Kotlin JSON parsing improvement

Posted on

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.

Leave a Reply

Your email address will not be published. Required fields are marked *