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 the Nothing
was not used
{"value":42}
The next chapter covers Serializers.