The Kotlin serialization library provides APIs for working with JVM streams and kotlinx-io or Okio sources and sinks.
You can use these APIs to serialize and deserialize JSON directly from I/O sources without creating intermediate strings. These APIs use UTF-8 encoding and throw SerializationException for invalid JSON data and IOException for I/O failures.
When working with I/O resources, it's important to close them properly to prevent resource leaks. You can do this with the .use() function, which closes the resource automatically when the operation completes.
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.io.FileOutputStream
@Serializable
data class Project(val name: String, val stars: Int)
fun main() {
val project = Project("kotlinx.serialization", 9000)
// Creates an OutputStream for the project.json file
FileOutputStream("project.json").use { output ->
// Serializes the project instance into the OutputStream
Json.encodeToStream(project, output)
}
}
In this example, the JSON representation of Project is serialized into the project.json file.
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.io.FileInputStream
@Serializable
data class Project(val name: String, val stars: Int)
fun main() {
// Opens an InputStream
FileInputStream("project.json").use { input ->
// Deserializes the JSON contents of the InputStream into a Project instance
val project = Json.decodeFromStream<Project>(input)
// Prints the deserialized Project instance
println(project)
}
}
In this example, the JSON contents of the input stream are deserialized into a single Project instance.
If your input contains multiple JSON objects in a top-level JSON array or as whitespace-separated objects, you can use .decodeToSequence() to process the elements lazily. This lets you handle each value as it is parsed, for example:
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.io.FileInputStream
@Serializable
data class Project(val name: String, val stars: Int)
fun main() {
// Opens an InputStream for the projects.json file containing a JSON array of Project objects
FileInputStream("projects.json").use { input ->
// Lazily deserializes each Project from the InputStream
val projects = Json.decodeToSequence<Project>(input)
// Processes elements one by one
for (project in projects) {
println(project)
}
}
}
JSON serialization with kotlinx-io and Okio
In addition to JVM streams, you can work with JSON using I/O types, such as kotlinx.io.Sink and kotlinx.io.Source from the kotlinx-io library (currently in Alpha), and okio.BufferedSink and okio.BufferedSource from the Okio library.
You can use the following Json extension functions to read and write JSON directly through these I/O types:
The next sections cover examples using kotlinx-io types with these APIs. You can use Okio types similarly with their corresponding okio.BufferedSink and okio.BufferedSource APIs.
Add dependencies for kotlinx-io and Okio
To use the extension functions with kotlinx-io or Okio types, add the corresponding dependencies:
To serialize JSON to a Sink, use the .encodeToSink() function:
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// Imports declarations for kotlinx-io types and JSON I/O support
import kotlinx.serialization.json.io.*
import kotlinx.io.*
import kotlinx.io.files.*
@Serializable
data class Project(val name: String, val stars: Int)
@OptIn(ExperimentalSerializationApi::class)
fun main() {
val project = Project("kotlinx.serialization", 9000)
// Creates a Sink for the project.json file
val path = Path("project.json")
SystemFileSystem.sink(path).buffered().use { sink: Sink ->
// Serializes the Project instance directly into a Sink
Json.encodeToSink(project, sink)
}
}
Deserialize JSON from Sources
To deserialize JSON from a Source, use the .decodeFromSource() function:
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// Imports declarations for kotlinx-io types and JSON I/O support
import kotlinx.serialization.json.io.*
import kotlinx.io.*
import kotlinx.io.files.*
@Serializable
data class Project(val name: String, val stars: Int)
@OptIn(ExperimentalSerializationApi::class)
fun main() {
// Opens a Source for the project.json file
val path = Path("project.json")
SystemFileSystem.source(path).buffered().use { source: Source ->
// Deserializes a Project instance directly from a Source
val project = Json.decodeFromSource<Project>(source)
println(project)
}
}
If your input contains a large JSON array or multiple top-level JSON objects, you can turn a Source into a lazily decoded Sequence<T> with the .decodeSourceToSequence() function.
// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// Imports declarations for kotlinx-io types and JSON I/O support
import kotlinx.serialization.json.io.*
import kotlinx.io.*
import kotlinx.io.files.*
@Serializable
data class Project(val name: String, val stars: Int)
@OptIn(ExperimentalSerializationApi::class)
fun main() {
// Opens a Source for the projects.json file containing multiple JSON objects
val path = Path("projects.json")
SystemFileSystem.source(path).buffered().use { source: Source ->
// Lazily deserializes each Project as it is read from the Source
val projects: Sequence<Project> = Json.decodeSourceToSequence(source)
for (project in projects) {
println(project)
}
}
}
What's next
Learn how to customize Json instances to address different use cases for serialization and deserialization.