1

I want to be able to store and obtain serialized List of objects using GSON.

However when I am trying to obtain the List I am getting weird structure of ArrayList of LinkedTreeMaps (each for the object property)

Here is my method to store something to prefs storage:

  fun <T> putAsJson(key: String, valueObject: T?) {
    if (valueObject != null) {
      put(key, gson.toJson(valueObject))
    } else {
      put(key, null)
    }
  }

And here is how I obtain it:

  inline fun <reified T: Any> getFromJson(key: String): T? {
    val jsonValue = get<String>(key)
    return try {
      gson.fromJson(jsonValue, T::class.java)
    } catch (ex: Exception) {
      Timber.e(ex, "Error when parsing JSON representing ${T::class.java} class")
      null
    }
  }

So i simply store it as:

fun saveSomeList(list: List<SomeObject>?) {
    someStorage.putAsJson(KEY, list)
  }

And then I try to obtain it using this method:

  fun getSomeList(): List<SomeObject> {
    return someStorage.getFromJson<List<SomeObject>>(KEY) ?: emptyList()
  }

What I am doing wrong?

K.Os
  • 5,123
  • 8
  • 40
  • 95

1 Answers1

1

TypeTokens for Generic-Type Serialization

For collection (and other generic) types, GSON cannot extract the correct type automatically. You need to help it by providing a "Type Token" :

// Known list
val ints = listOf(1, 2, 3)
val collectionType = object : TypeToken<Collection<Int>>() {}.type
val json = gson.toJson(ints, collectionType)
val ints2: List<Int> = gson.fromJson(json, collectionType)

// Known generic
val foo = Foo<Bar>()
val fooType = object : TypeToken<Foo<Bar>>() {}.type
val json = gson.toJson(foo, fooType)
val foo2: Foo<Bar> = gson.fromJson(json, fooType)

If you want to maintain the genericity of your inner type, i.e. the T in List<T>, you can do this:

// Unknown generic
val modelType = T::class.java
val listType = TypeToken.getParameterized(List::class.java, modelType).type
val list = gson.fromJson(jsonValue, listType)

Your Specific Case

I think your best option is a specific method for lists:

inline fun <reified T: Any> getListFromJson(key: String): T? {
    val jsonValue = get<String>(key)
    return try {
        val listType = TypeToken.getParameterized(List::class.java, T::class.java).type
        gson.fromJson(jsonValue, listType)
    } catch (ex: Exception) {
        Timber.e(ex, "Error when parsing JSON representing List<${T::class.java}> class")
        null
    }
}

Sources:
- https://github.com/google/gson/blob/master/UserGuide.md#TOC-Collections-Examples
- https://stackoverflow.com/a/44303909/2957169

charles-allen
  • 3,891
  • 2
  • 23
  • 35