Home > Mobile >  How to use Gson to deserialize a json array string to object mode using generic class type?
How to use Gson to deserialize a json array string to object mode using generic class type?

Time:11-01

Having a function for deserializing json string into object mode by providing the class type the json string should be deserialized into. It works for most of the case

    fun <T> deserializeJsonStr(jsonString: String, dataClassType: Class<T>):T? {
        var ret: T? = Gson().fromJson<T>(jsonString, dataClassType)
        return ret
    }

like

// data modle
class TestData1 {
    var userId = 0
    var id = 0
    var title: String? = null
    var body: String? = null
}
// the json string
{
      "userId": 1,
      "id": 3,
      "title": "the title",
      "body": "the body"
    }

// call the function
val clazz: Class<TestData1> = TestData1::class.java
val theTestData1 = deserializeJsonStr(jsonString, clazz)

it will return the object theTestData1 with the fields filled.

But if the json string is for json array:

[
  {
    "userId": 1,
    "id": 1,
    "title": "title1",
    "body": "body1"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "title2",
    "body": "body2"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "title3",
    "body": "body3"
  }
]

Though it should be ArrayList<TestData1>, but what is it class type? tried:

val clazz: Class<ArrayList<TestData1>> = ArrayList<TestData1>::class.java
val theTestData1 = psreJsonStr(jsonString, clazz)

but it does not compile: enter image description here or the val pojoClass: Class<ArrayList<TestData1>> = ArrayList<>::class.java does not compile either: enter image description here

what would be the data class mode for this json array? or what is the Class<T> required by the function param for ArrayList?

CodePudding user response:

This is related to type erasure: ArrayList<TestData1>::class.java is not possible because at runtime the type argument of ArrayList is not available, so this would basically be Class<ArrayList>1. Therefore the compiler does not allow this because it would not be safe.

Gson has its TypeToken class to work around this, see also the user guide.

In your case you could change your deserializeJsonStr to the following:

fun <T> deserializeJsonStr(jsonString: String, dataType: TypeToken<T>): T? {
    // Note: Older Gson versions might require using `dataType.type`
    return Gson().fromJson(jsonString, dataType)
}

You would then call the function like this:

val type = object: TypeToken<ArrayList<TestData1>>() {}
val data = deserializeJsonStr(jsonString, type)

You could additionally add an overload of deserializeJsonStr with reified type parameter which makes it make more convenient to use:

inline fun <reified T> deserializeJsonStr(jsonString: String): T? {
    return deserializeJsonStr(jsonString, object: TypeToken<T>() {})
}
// Note: You can even omit the explicit `ArrayList<TestData1>` if the compiler can infer it
val data = deserializeJsonStr<ArrayList<TestData1>>(jsonString, type)

1 Class is a bit special because its direct type argument is not erased at runtime, for example Class<ArrayList> is not erased to Class<Object>. But for every other generic type the type argument is erased.

  • Related