Kotlinx.serialization part2
By: Peter Nagy
Tags: Android, Json, Kotlin, Serialization

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! :)