35

I have a simple Kotlin program that access a Mongo database and produce a JSON string as below;

"{
     "_id" : { "$oid" : "593440eb7fa580d99d1abe85"} , 
     "name" : "Firstname Secondname" ,
     "reg_number" : "ATC/DCM/1016/230" ,
     "oral" : 11 ,
     "oral_percent" : 73 , 
     "cat_1" : 57 , 
     "cat_2" : 60 , 
     "cat_average" : 59 , 
     "assignment" : 90
}"

How do I map this in Kotlin Map/MutableMap? Is there an API in Kotlin to read JSON and map it to Map/MutableMap?

1
  • 1
    Look at GSON Commented Jul 2, 2017 at 12:57

6 Answers 6

41

No additional library is needed:

val jsonObj = JSONObject(jsonString)
val map = jsonObj.toMap()

where toMap is:

fun JSONObject.toMap(): Map<String, *> = keys().asSequence().associateWith {
    when (val value = this[it])
    {
        is JSONArray ->
        {
            val map = (0 until value.length()).associate { Pair(it.toString(), value[it]) }
            JSONObject(map).toMap().values.toList()
        }
        is JSONObject -> value.toMap()
        JSONObject.NULL -> null
        else            -> value
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

That seems way more Kotlin'esque - upvoted. And the 'value.toMap()' recursive call was funny as hell. Also, that when-is conditional, never seen that before. Learning so much from one post, you deserve at least two upvotes for every single upvote.
Awesome answer. I would only replace the JSONArray part with (0 until value.length()).map { value[it] } - elegant and concise.
@RomanKotenko i couldn't because JSONObject takes only maps, not lists. I have to use .toMap recursively, in order to convert JSONObject.NULL to null
@muzzletov ahahaha thank you
sometimes import statements help. No clue where to get JSONObject from
|
14

This can be done with Klaxon. With this you can easily read the Json data as JsonObject which is actually a MutableMap.

val json: JsonObject = Parser().parse(jsonData) as JsonObject

1 Comment

Parser() is deprecated. the lib recommands to use Parser.default() instead
13

This is now also possible with kotlinx.serialization:

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject

val input = """{
     "_id" : { "some_id" : "593440eb7fa580d99d1abe85"} , 
     "name" : "Firstname Secondname" ,
     "reg_number" : "ATC/DCM/1016/230" ,
     "oral" : 11 ,
     "oral_percent" : 73 , 
     "cat_1" : 57 , 
     "cat_2" : 60 , 
     "cat_average" : 59 , 
     "assignment" : 90
}"""

val json = Json.parseToJsonElement(input)

val map = json.jsonObject.toMap()

3 Comments

it creates Map<String, JsonElement> but I need Map<String, Any>. How can I do so?
@philoopher97 you can cast the results with as Map<String, Any>, although that would be a mistake in most cases.
Thank you. Thanks to you, I solved it easily. However, you need to add the following in the build.gradle's dependencies. implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" // JVM dependency
9

Using Jackson's kotlin module, you can create a Map/MutableMap as below:

val jsonString = "{\n" +
            "  \"_id\": {\n" +
            "    \"\$oid\": \"593440eb7fa580d99d1abe85\"\n" +
            "  },\n" +
            "  \"name\": \"Firstname Secondname\",\n" +
            "  \"reg_number\": \"ATC/DCM/1016/230\",\n" +
            "  \"oral\": 11,\n" +
            "  \"oral_percent\": 73,\n" +
            "  \"cat_1\": 57,\n" +
            "  \"cat_2\": 60,\n" +
            "  \"cat_average\": 59,\n" +
            "  \"assignment\": 90\n" +
            "}"

val map = ObjectMapper().readValue<MutableMap<Any, Any>>(jsonString)

Note: In case you're getting the below compilation error

None of the following functions can be called with the arguments supplied

Please ensure that you have added the the dependency of jackson-module-kotlin (for gradle: compile "com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}") and have added the import for the readValue implementation as import com.fasterxml.jackson.module.kotlin.readValue in the place where you're using readValue

3 Comments

i get an error for "readValue" that says "None of the following functions can be called with the arguments supplied."
@Raksha did that help?
ObjectMapper().readValue(jsonString, MutableMap::class.java)
5

This can be done without any third party library:

@Throws(JSONException::class)
fun JSONObject.toMap(): Map<String, Any> {
    val map = mutableMapOf<String, Any>()
    val keysItr: Iterator<String> = this.keys()
    while (keysItr.hasNext()) {
        val key = keysItr.next()
        var value: Any = this.get(key)
        when (value) {
            is JSONArray -> value = value.toList()
            is JSONObject -> value = value.toMap()
        }
        map[key] = value
    }
    return map
}

@Throws(JSONException::class)
fun JSONArray.toList(): List<Any> {
    val list = mutableListOf<Any>()
    for (i in 0 until this.length()) {
        var value: Any = this[i]
        when (value) {
            is JSONArray -> value = value.toList()
            is JSONObject -> value = value.toMap()
        }
        list.add(value)
    }
    return list
}

Usage to convert JSONObject to Map:

val jsonObject = JSONObject(jsonObjStr)
val map = jsonObject.toMap()

Usage to convert JSONArray to List:

val jsonArray = JSONArray(jsonArrStr)
val list = jsonArray.toList()

More info is here

Comments

5

The latest version of the org.json library has JSONObject.toMap() so you can just do

val map = JSONObject(string).toMap()

So just add the org.json:json:20220924 dependency to your project.

If you're writing for Android (and thus have an old version of the org.json library) then the following can be used.

import org.json.JSONArray
import org.json.JSONObject

fun JSONObject.toMap(): Map<String, Any?> =
    keys().asSequence().associateWith { key -> toValue(get(key)) }

fun JSONArray.toList(): List<Any?> =
    (0 until length()).map { index -> toValue(get(index)) }

private fun toValue(element: Any) = when (element) {
    JSONObject.NULL -> null
    is JSONObject -> element.toMap()
    is JSONArray -> element.toList()
    else -> element
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.