Kotlinx.serialization part2

Peter Nagy
3 min readJan 22, 2021

This is a series, you can find the first part of the story:

In the part1 article I have written about basic of the kotlinx.serialization.

As you know when you want to serialize or deserialize an object then we need a Json object. What is this ?

  • Json is a configuration holder object

If you search in the Internet you can find a lot of article where there is a different syntax (JSON, or JsonConfiguration). The reason is API change in the RC release. :( Earlier there was a public JsonConfiguration object which is now private and there is a builder if you want to set some configuration variable.

Json {
encodeDefaults = true
ignoreUnknownKeys = true
isLenient = true
allowStructuredMapKeys = true
prettyPrint = true
prettyPrintIndent = " "
coerceInputValues = true
useArrayPolymorphism = true
classDiscriminator = ""
allowSpecialFloatingPointValues = true
serializersModule = SerializersModule { }
}

You can find the documentation here:

encodeDefaults

By default kotlinx will not encode the default value(s).

@Serializable
data class Data(val text: String? = null, val text2: String? = null, val text3: String = "foo")
val data = Data()val encodedString = Json.encodeToString(data)

If you print encodedString it will be { }. Ok, when we deserialize it -> it will be same object however sometimes we need the null value (e.g. our backend will handle it)

Solution:

val encodedString = Json { encodeDefaults = true }.encodeToString(data)

The result will be: {“text”:null,”text2":null,”text3":”foo”}

ignoreUnknownKeys

JSON format often is used in network communication. It is a common use case we have an object in our application and it has been changed in current version of the network call. (Object version is changed on backend)

@Serializable
data class Data(val userName: String)
val obj = Json.decodeFromString<Data>("{\"userName\": \"fooUser\", \"city\": \"Budapest\"}")

If we run the code above then we will get an exception:

kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 42: Encountered an unknown key 'city'.
Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys.
JSON input: {"userName": "fooUser", "city": "Budapest"}

The error message will show the solution we need to turn on ignoreUnknownKeys setting to parse the object.

isLenient

By default there are some restriction in JSON format (https://www.ietf.org/rfc/rfc4627.txt). Keys must be quoted, literals shall be unquoted.

Can we parse this string “{userName: fooUser}” ?

With help of isLenient we are able to parse a string much more flexibility.

@Serializable
data class Data(val userName: String)
Json { isLenient = true }.decodeFromString<Data>("{userName: fooUser}")

Without this settings we will get an exception:

kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 1: Expected string literal with quotes.
Use 'isLenient = true' in 'Json {}` builder to accept non-compliant JSON.
JSON input: {userName: fooUser}

allowStructuredMapKeys

By default JSON format do not allow to use map with structured map.

@Serializable
data class Data(val userName: String)
Json.encodeToString(mapOf(Data("test") to 1, Data("testX") to 2))

If we want to encode a map with object type key (structured object) we need to set allowStructuredMapKeys settings.

prettyPrint

Sometimes we want to see a well readable JSON instead of one line of String. With help of prettyPrint settings we will get a human readable text. By default there will be 4 spaces indent. If we want to setup different indention we can set with prettyPrintIndent option.

This option could be very useful when we want to print a JSON string in a log file or Logcat.

coerceInputValues

Kotlin has some very useful language feature like default value. This feature will help us to avoid to use null value like a valid value. Ok, and how can we use this feature when backend can send us null value?

@Serializable
data class Data(val userName: String, val city: String = "nowhere")
val data = Json.decodeFromString<Data>("{\"userName\": \"test\", \"city\": null}")val data2 = Json.decodeFromString<Data>("{\"userName\": \"test\"}")

The second object data2 will be parsed successful and city will hold the default value as we expected. However the first object (data) will cause an exception because there is a null type in the JSON. If we want to avoid this kind of exception we need to set coerceInputValues = true.

allowSpecialFloatingPointValues

By default JSON do not support special floating point values like NaN (Not a Number) or infinities.

@Serializable
data class Data(val number: Double)
Json { allowSpecialFloatingPointValues = true }.encodeToString(Data(Double.NaN))

And the result will be: {“number”:NaN}

In next part I will write about how can we parse some extra data object like Date or BigDecimal:

If you found this post useful? Kindly tap the 👏 button below and follow, thanks! :)

--

--