This is the second chapter of the Kotlin Serialization Guide. In addition to all the primitive types and strings, serialization for some classes from the Kotlin standard library, including the standard collections, is built into Kotlin Serialization. This chapter explains the details.
Table of contents
Kotlin Serialization has the following ten primitives:
Boolean
, Byte
, Short
, Int
, Long
, Float
, Double
, Char
, String
, and enums.
The other types in Kotlin Serialization are composite—composed of those primitive values.
All types of integer and floating-point Kotlin numbers can be serialized.
@Serializable
class Data(
val answer: Int,
val pi: Double
)
fun main() {
val data = Data(42, PI)
println(Json.encodeToString(data))
}
You can get the full code here.
Their natural representation in JSON is used.
{"answer":42,"pi":3.141592653589793}
Long integers are serializable, too.
@Serializable
class Data(val signature: Long)
fun main() {
val data = Data(0x1CAFE2FEED0BABE0)
println(Json.encodeToString(data))
}
You can get the full code here.
By default they are serialized to JSON as numbers.
{"signature":2067120338512882656}
The JSON output from the previous example will get decoded normally by Kotlin Serialization running on Kotlin/JS. However, if we try to parse this JSON by native JavaScript methods, we get this truncated result.
JSON.parse("{\"signature\":2067120338512882656}")
▶ {signature: 2067120338512882700}
The full range of a Kotlin Long does not fit in the JavaScript number, so its precision gets lost in JavaScript.
A common workaround is to represent long numbers with full precision using the JSON string type.
This approach is optionally supported by Kotlin Serialization with LongAsStringSerializer, which
can be specified for a given Long property using the @Serializable
annotation:
@Serializable
class Data(
@Serializable(with=LongAsStringSerializer::class)
val signature: Long
)
fun main() {
val data = Data(0x1CAFE2FEED0BABE0)
println(Json.encodeToString(data))
}
You can get the full code here.
This JSON gets parsed natively by JavaScript without loss of precision.
{"signature":"2067120338512882656"}
The section on Specifying serializers for a file explains how a serializer like
LongAsStringSerializer
can be specified for all properties in a file.
All enum classes are serializable out of the box without having to mark them @Serializable
,
as the following example shows.
// The @Serializable annotation is not needed for enum classes
enum class Status { SUPPORTED }
@Serializable
class Project(val name: String, val status: Status)
fun main() {
val data = Project("kotlinx.serialization", Status.SUPPORTED)
println(Json.encodeToString(data))
}
You can get the full code here.
In JSON an enum gets encoded as a string.
{"name":"kotlinx.serialization","status":"SUPPORTED"}
Note: On Kotlin/JS and Kotlin/Native,
@Serializable
annotation is needed for enum class if you want to use it as a root object — i.e. useencodeToString<Status>(Status.SUPPORTED)
.
Serial names of enum entries can be customized with the SerialName annotation just like
it was shown for properties in the Serial field names section.
However, in this case, the whole enum class must be marked with the @Serializable
annotation.
@Serializable // required because of @SerialName
enum class Status { @SerialName("maintained") SUPPORTED }
@Serializable
class Project(val name: String, val status: Status)
fun main() {
val data = Project("kotlinx.serialization", Status.SUPPORTED)
println(Json.encodeToString(data))
}
You can get the full code here.
We see that the specified serial name is now used in the resulting JSON.
{"name":"kotlinx.serialization","status":"maintained"}
A number of composite types from the standard library are supported by Kotlin Serialization.
The simple data classes Pair and Triple from the Kotlin standard library are serializable.
@Serializable
class Project(val name: String)
fun main() {
val pair = 1 to Project("kotlinx.serialization")
println(Json.encodeToString(pair))
}
You can get the full code here.
{"first":1,"second":{"name":"kotlinx.serialization"}}
Not all classes from the Kotlin standard library are serializable. In particular, ranges and the Regex class are not serializable at the moment. Support for their serialization may be added in the future.
A List of serializable classes can be serialized.
@Serializable
class Project(val name: String)
fun main() {
val list = listOf(
Project("kotlinx.serialization"),
Project("kotlinx.coroutines")
)
println(Json.encodeToString(list))
}
You can get the full code here.
The result is represented as a list in JSON.
[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
Other collections, like Set, are also serializable.
@Serializable
class Project(val name: String)
fun main() {
val set = setOf(
Project("kotlinx.serialization"),
Project("kotlinx.coroutines")
)
println(Json.encodeToString(set))
}
You can get the full code here.
Set is also represented as a list in JSON, like all other collections.
[{"name":"kotlinx.serialization"},{"name":"kotlinx.coroutines"}]
During deserialization, the type of the resulting object is determined by the static type that was specified in the source code—either as the type of the property or as the type parameter of the decoding function. The following example shows how the same JSON list of integers is deserialized into two properties of different Kotlin types.
@Serializable
data class Data(
val a: List<Int>,
val b: Set<Int>
)
fun main() {
val data = Json.decodeFromString<Data>("""
{
"a": [42, 42],
"b": [42, 42]
}
""")
println(data)
}
You can get the full code here.
Because the data.b
property is a Set, the duplicate values from it disappeared.
Data(a=[42, 42], b=[42])
A Map with primitive or enum keys and arbitrary serializable values can be serialized.
@Serializable
class Project(val name: String)
fun main() {
val map = mapOf(
1 to Project("kotlinx.serialization"),
2 to Project("kotlinx.coroutines")
)
println(Json.encodeToString(map))
}
You can get the full code here.
Kotlin maps in JSON are represented as objects. In JSON object keys are always strings, so keys are encoded as strings even if they are numbers in Kotlin, as we can see below.
{"1":{"name":"kotlinx.serialization"},"2":{"name":"kotlinx.coroutines"}}
It is a JSON-specific limitation that keys cannot be composite. It can be lifted as shown in the Allowing structured map keys section.
The Kotlin builtin Unit type is also serializable.
Unit
is a Kotlin singleton object,
and is handled equally with other Kotlin objects.
Conceptually, a singleton is a class with only one instance, meaning that state does not define the object, but the object defines its state. In JSON, objects are serialized as empty structures.
@Serializable
object SerializationVersion {
val libraryVersion: String = "1.0.0"
}
fun main() {
println(Json.encodeToString(SerializationVersion))
println(Json.encodeToString(Unit))
}
You can get the full code here.
While it may seem useless at first glance, this comes in handy for sealed class serialization, which is explained in the Polymorphism. Objects section.
{}
{}
Serialization of objects is format specific. Other formats may represent objects differently, e.g. using their fully qualified names.
Since Kotlin 1.7.20
the Duration class has become serializable.
fun main() {
val duration = 1000.toDuration(DurationUnit.SECONDS)
println(Json.encodeToString(duration))
}
You can get the full code here.
Duration is serialized as a string in the ISO-8601-2 format.
"PT16M40S"
By default, Nothing is a serializable class. However, since there are no instances of this class, it is impossible to encode or decode its values - any attempt will cause an exception.
This serializer is used when syntactically some type is needed, but it is not actually used in serialization. For example, when using parameterized polymorphic base classes:
@Serializable
sealed class ParametrizedParent<out R> {
@Serializable
data class ChildWithoutParameter(val value: Int) : ParametrizedParent<Nothing>()
}
fun main() {
println(Json.encodeToString(ParametrizedParent.ChildWithoutParameter(42)))
}
You can get the full code here.
When encoding, the serializer for Nothing
was not used
{"value":42}
The next chapter covers Serializers.