3

My example code is below:

fun main(args: Array<String>) {
    val testData = mapOf<String, Any>(
        "name" to "albert",
        "age" to 26,
        "work" to listOf("1", "2", "3")
    )

    var value = JSON.stringify(testData, { _, value -> value.toString() }, 2)

    println(value)
}

The result is "{name=albert, age=26, work=[1, 2, 3]}". Seems it misses all the double quotes around the property name and string value.

I'm using KotlinJS rather than Kotlin

So, how to solve this?

Albert Gao
  • 3,653
  • 6
  • 40
  • 69
  • I played some time with kotlin and... Not 100% sure that i'm right, but actually what this code is doing is calling java `Map.toString()` and then gives this value to js `JSON.stringify`, so `"{name=albert, age=26, work=[1, 2, 3]}"` is not json, but map.toString() and `JSON.stringify` just adds quotes around this string. What you could do is to convert test data with jackson or gson in java and just forget about JSON or you could try https://github.com/Kotlin/kotlinx.serialization Also take a look at https://discuss.kotlinlang.org/t/kotlin-serialization/2063/25 – varren Sep 18 '17 at 03:42
  • @varren Thanks very much for the help. It's a full KotinJS environment, so can't take advantage from JVM world :( I tried `println(JSON.stringify(testData.toString()))`, still no quotes. According to the posts you share, seems that built-in serialization things haven't land to KotlinJS world yet. I will look into 3rd party libs as you said, thanks. – Albert Gao Sep 18 '17 at 03:56

4 Answers4

3

Actually, you don't get the result of JSON.stringify. Instead, you get result of kotlin.collections.HashMap.toString. The reason is following: you pass the lambda as a second parameter: { _, value -> value.toString() }. This converts your entire map to string, using, toString() function. And HashMap.toString function is defined to generate such string, which is not a JSON string. You should have used JSON.stringify without second and third parameter. However, this won't work as well, producing Uncaught TypeError: Converting circular structure to JSON error. The reason is following: JSON.stringify is not part of Kotlin language, it's just a typed definition of a native browser object, called JSON. HashMap is not an empty JavaScript object, it allows using any types of objects as keys, it exposes Java-like Map API, which is unavailable in JavaScript object. So, HashMap is not suitable for what you doing. There are several solutions:

  1. You can wait until we publish Kotlin multiplatform serilization, see the corresponding discussion. This API is capable of understanding Kotlin clases and converting them to JSON properly.

  2. Don't use Kotlin maps and lists, use native JavaScript entities, like json and pure arrays. Your example can be rewritten the following way:

    import kotlin.js.json

    fun main(args: Array<String>) {
        val testData = json(
            "name" to "albert",
            "age" to 26,
            "work" to arrayOf("1", "2", "3")
        )
    
        var value = JSON.stringify(testData)
    
        println(value)
    }
    
Alexey Andreev
  • 1,980
  • 2
  • 17
  • 29
  • Much better, ok for my current usage, thanks! Waiting for you guys to release :) – Albert Gao Sep 18 '17 at 11:05
  • Could we iterate through the Json object's properties? – Albert Gao Sep 19 '17 at 02:09
  • You can call `Object.keys()` or `Object.getOwnPropertyNames()`, either as inline JS or define these methods as external. – Alexey Andreev Sep 19 '17 at 07:59
  • Thanks. But those are JS methods right? The data I got is `kotlin.js.Json`, I tried using `(data as dynamic).keys()`, cause an error: Dynamic types are not allowed in this position :( – Albert Gao Sep 19 '17 at 10:56
  • The proper syntax is `data.asDynamic().keys()`. But this won't work, since `keys()` is not a member function. You should write something like js("Object.keys(data)") or external class Object { companion object { fun keys(value: Any): Array } } Object.keys(data) – Alexey Andreev Sep 19 '17 at 11:02
1

This answer comes a few years after the previous accepted answer, but I (who am new to Kotlin) ran into this same problem, and figured out a nice way to achieve this.

  1. Assume you have a populated Map
  2. Create an empty js object (seems hacky to me, but this is Kotlin to JS)
  3. iterate over the your map's "toList" method which provides a Pair
  4. insert each pair into js object
  5. grab the js JSON api
  6. and call "stringify" providing your js object. Remember to "toString" at the end

    val data: Map<String, Any?> = mapOf() // 1
    val export = js("{}") // 2
    for (pair: Pair<String, Any?> in data.toList()) { // 3
        export[pair.first] = pair.second // 4
    }
    val jsoner = js("JSON") // 5
    return jsoner.stringify(export).toString() // 6
    
jabgibson
  • 416
  • 4
  • 14
0

If you wish to simply retrieve a valid JSON string from a Kotlin Map, you can do so using kotlinx.serialization.json:

Json.encodeToString(map)

If you would like to pretty stringify a Kotlin Map, which can be displayed as a pretty JSON in a <pre> tag, you can do:

val jsonString = Json.encodeToString(map)            // 1
val jsonObject = JSON.parse<JsonObject>(jsonString)  // 2
return JSON.stringify(jsonObject, null, 2)           // 3
  1. Serialize the map to a JSON string using Kotlin serialization
  2. Parse the JSON string to a Javascript object
  3. Pretty stringify the Javascript object
skr
  • 21
  • 2
0

With recent versions of kotlinx-serialization, use encodeToDynamic:

val json = Json.encodeToDynamic(map)

The result is a dynamic that contains the JSON object. There are some limitations of this approach — for example, all map keys must be primitives or enums and Long values have a maximum value. See the docs for more details.

Raman
  • 17,606
  • 5
  • 95
  • 112