bookmark_borderDeserialise Json to typed Map with Jackson in Kotlin

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: