Categories
Kotlin

Create JSON manually with kotlinx.serialization

Kotlin serialization is a great library for serialisation in Kotlin. It is mainly geared towards serialising from objects to strings and back, but on closer look it also contains a comprehensive Json library. Even after discovering the documentation, though, the use of this new library might be confusing.

🕋 Serialisation to objects

Consider a data class:

@Serializable
data class Credentials(
	val publicKey: String,
	val privateKey: String,
)

The @Serializable annotation enables the encoding to string and back:

val credentials = Credentials("publicKey", "privateKey")

val stringValue = Json.encodeToString(credentials)
println(stringValue)

val credentialsDecoded = Json.decodeFromString<Credentials>(stringValue)
println(credentialsDecoded.publicKey)

/* 
output:
	{"publicKey":"publicKey","privateKey":"privateKey"}
	Credentials(publicKey=publicKey, privateKey=privateKey)
*/

With object serialisation, the the library shines with its ease of use. With JSON, however, the use becomes more ambiguous.

🎞 Serialisation to JSON string, manually

When creating a web requests, a separate class for posting the data is not required. Then, the request body can be created with the JSON features part of the serialisation library. In there, comprehensive function set exists to handle most JSON encoding problems.

Creating our credentials string, for example, would look like:

val credentials = JsonObject(
    mapOf(
        "publicKey" to JsonPrimitive("publicKey"),
        "privateKey" to JsonPrimitive("privateKey")
    )
)

val array = JsonArray(listOf(credentials))
println(array.toString())

/* 
output:
	[{"publicKey":"publicKey","privateKey":"privateKey"}]
*/

There is also a DSL version of this construction, which might be preferred:


val credentials = buildJsonArray {
    addJsonObject {
        put("publicKey", "publickey")
        put("privateKey", "privateKey")
    }
}

println(credentials.toString())

/*
output:
	[{"publicKey":"publickey","privateKey":"privateKey"}]
*/

🌐 Serialisation for YAML and other formats

Only JSON JSON, and some experimental formats, are supported out of the box. For others, like YAML, an external library that implements a custom formatter, can be used.

With this dependency, a YAML string can decoded into a @Serializable object as follows:

val yamlEncoded =
    """
        publicKey: "publicKey"
        privateKey: "privateKey"
    """.trimIndent()

val credentials =
    Yaml.default.decodeFromString(
        Credentials.serializer(),
        yamlEncoded
    )

println(credentials)

/*
output:
	Credentials(publicKey=publicKey, privateKey=privateKey)
*/

🌸 Pretty printing a JSON string

If JSON input is without line breaks, it can be useful to make it more human readable. The JSON library can then be utilised with the prettyPrint property.

val format = Json { prettyPrint = true }
val input = """
    {"publicKey":"publicKey","privateKey":"privateKey"}
""".trimIndent()

val jsonElement = format.decodeFromString<JsonElement>(input)
val bodyInPrettyPrint = format.encodeToString(jsonElement)

println(bodyInPrettyPrint)
/*
output:
    {
        "publicKey": "publicKey",
        "privateKey": "privateKey"
    }
*/

As shown here, the input string needs to be decoded into a JsonElement, and then encoded back to string again. Only then, the prettyPrint property will cooperate in achieving our goal.

⌛️ Conclusion

Kotlinx.serialization is a great tool for serialising objects and parsing JSON strings. Since it is a new library within a new language, all of the features might not be obvious at first. Therefore, analysis of the documentation is encouraged before using it in code.

Sample code is available in tonisives repo.

Related tweet: