There are multiple resources how to deserialise JSON to a typed object. This is fairly straightforward. But what if have a blob of JSON like this:
{
"FOO": 1,
"BAR": 2,
"BAZ": 3
}
And the keys of this map would be defined in an enum.
Surely you could deserialise this into a Map<String, String>
first, or simply a Map
. And *after* that trying to map your keys to the enum
you use.
But what if you want to do something like this?
Map<MyEnum, Integer> map = deserialiseJsonAsMap(json);
Well you’d need something with generics ? Too bad: you can’t do this in Java due to type erasure.
In Kotlin however you *can* do this. Your deserialiseJsonAsMap
function should use inline
and reified
to make it happen.
In short, your function could look like this:
@Throws(JsonProcessingException::class, JsonMappingException::class)
inline fun <reified K, V> deserialiseJsonAsMap(jsonData: String?): Map<K, V> {
if (StringUtils.isEmpty(jsonData)) {
return HashMap<K, V>()
}
val typeRef: TypeReference<Map<K, V>> = object : TypeReference<Map<K, V>>() {}
return objectMapper.readValue(jsonData, typeRef)
}
You could omit the StringUtils.isEmpty()
check if you want to.
Because of inline
the method body gets inlined everywhere it is called. And using reified
we make sure the types are used properly. Ie, if your calling code:
var map = getJsonValuesAsMapFriendly<MyEnum, Int>(json)
It basically makes it like this:
val map = if (StringUtils.isEmpty(json)) {
HashMap<MyEnum, Int>()
} else {
val typeRef: TypeReference<Map<MyEnum, Int>> = object : TypeReference<Map<MyEnum, Int>>() {}
objectMapper.readValue(json, typeRef)
}
This means your code gets bigger (due the inline
) but you do have a single function now!
See also:
- Github ticket with my investigation
- Stackoverflow: here and here
- Kotlin docs