Parsing using GSON’s @JsonAdapter

Haris Krasniqi
MindOrks
Published in
2 min readMar 27, 2018

--

Most of you have heard about TypeAdapter which is used to customize JSON conversions. Sometimes API can be designed that way that it wont fit your needs on client side. Thus you have to modify the response.

Below an example of modifying the type of the response is shown. Specifically a List of Objects is parsed into HashMap

Suppose we expect this Json response:


"response": {
.... "values": [
{
"key": "foo_key",
"label": "foo_label"
},
{
"key": "foo_key_2",
"label": "foo_label_2"
}
]
}

From this response, normally it is expected that we parse it to List<Foo> values:

Response response

--

data class Response{
val values : List<Foo>
}

--

data class Foo{
val key : String
val label : String
}

but we don’t want to have that. As Foo object has a key and a label which looks very similar to keys and values on Map, we want to parse it to HashMap.

In order to do that we will need a TypeAdapter and customize our Json response based on out needs:

class FooTypeAdapter : TypeAdapter<HashMap<String, String>>() {

companion object {
private val KEY_NAME = "key"
private val LABEL_NAME = "label"
}

@Throws(IOException::class)
override fun write(writer: JsonWriter, values: HashMap<String, String>?) {
if (values == null) {
writer.nullValue()
return
}
writeMap(writer, values)
}

@Throws(IOException::class)
private fun writeMap(writer: JsonWriter, values: HashMap<String, String>) {
writer.beginArray()
for ((key, value) in values) {
writer.beginObject()
writer.name(KEY_NAME).value(key)
writer.name(LABEL_NAME).value(value)
writer.endObject()
}
writer.endArray()
}

@Throws(IOException::class)
override fun read(reader: JsonReader): HashMap<String, String>? {
try {
val token = reader.peek()
if (token == JsonToken.NULL) {
reader.nextNull()
return null
}

if (token == JsonToken.BEGIN_ARRAY) {
return readArray(reader)
}
} catch (e: IOException) {
return null
}

return HashMap()
}

@Throws(IOException::class)
private fun readArray(reader: JsonReader): HashMap<String, String> {
val map = HashMap<String, String>()
reader.beginArray()
while (reader.hasNext()) {
val pair = readObject(reader)
map[pair.first!!] = pair.second!!
}
reader.endArray()
return map
}

@Throws(IOException::class)
private fun readObject(reader: JsonReader): Pair<String, String> {
var key = ""
var label = ""
reader.beginObject()
while (reader.hasNext()) {
val name = reader.nextName()
when (name) {
KEY_NAME -> key = reader.nextString()
LABEL_NAME -> label = reader.nextString()
else -> reader.skipValue()
}
}
reader.endObject()
return Pair(key, label)
}
}

Now we have to register this adapter to Gson. One way is to register it when building Gson instance (.registerTypeAdapter(Foo::class, FooTypeAdapter())) and the other way is to directly use it on field (by @JsonAdapter). Using it directly on field on our example would be something like:

Response response

--

data class Response{
@JsonAdapter(FooTypeAdapter::class)
val values : HashMap<String, String>
}

Notice that now we have a HashMap<String, String> instead of a List<Foo>

If you liked this article, be sure to 👏 and check my Play Store Apps

--

--