Content negotiation and serialization in Ktor Server
The ContentNegotiation plugin serves two primary purposes:
Negotiating media types between the client and server. For this, it uses the Accept
and Content-Type
headers.
Serializing/deserializing the content in a specific format. Ktor supports the following formats out-of-the-box: JSON, XML, CBOR, and ProtoBuf.
On the client, Ktor provides the ContentNegotiation plugin for serializing/deserializing content.
Add dependencies ContentNegotiation To use ContentNegotiation
, you need to include the ktor-server-content-negotiation
artifact in the build script:
implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
implementation "io.ktor:ktor-server-content-negotiation:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-server-content-negotiation-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
Note that serializers for specific formats require additional artifacts. For example, kotlinx.serialization requires the ktor-serialization-kotlinx-json
dependency for JSON.
Serialization Before using kotlinx.serialization converters, you need to add the Kotlin serialization plugin as described in the Setup section.
JSON To serialize/deserialize JSON data, you can choose one of the following libraries: kotlinx.serialization, Gson, or Jackson.
Add the ktor-serialization-kotlinx-json
artifact in the build script:
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-kotlinx-json-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
Add the ktor-serialization-gson
artifact in the build script:
implementation("io.ktor:ktor-serialization-gson:$ktor_version")
implementation "io.ktor:ktor-serialization-gson:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-gson-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
Add the ktor-serialization-jackson
artifact in the build script:
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
implementation "io.ktor:ktor-serialization-jackson:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-jackson-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
XML To serialize/deserialize XML, add the ktor-serialization-kotlinx-xml
in the build script:
implementation("io.ktor:ktor-serialization-kotlinx-xml:$ktor_version")
implementation "io.ktor:ktor-serialization-kotlinx-xml:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-kotlinx-xml-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
CBOR To serialize/deserialize CBOR, add the ktor-serialization-kotlinx-cbor
in the build script:
implementation("io.ktor:ktor-serialization-kotlinx-cbor:$ktor_version")
implementation "io.ktor:ktor-serialization-kotlinx-cbor:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-kotlinx-cbor-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
ProtoBuf To serialize/deserialize ProtoBuf, add the ktor-serialization-kotlinx-protobuf
in the build script:
implementation("io.ktor:ktor-serialization-kotlinx-protobuf:$ktor_version")
implementation "io.ktor:ktor-serialization-kotlinx-protobuf:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-serialization-kotlinx-protobuf-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
Install ContentNegotiation To install the ContentNegotiation
plugin to the application, pass it to the install
function in the specified module . The code snippets below show how to install ContentNegotiation
...
... inside the embeddedServer
function call.
... inside the explicitly defined module
, which is an extension function of the Application
class.
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
fun main() {
embeddedServer(Netty, port = 8080) {
install(ContentNegotiation)
// ...
}.start(wait = true)
}
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
// ...
fun Application.module() {
install(ContentNegotiation)
// ...
}
Ktor supports the following formats out-of-the-box: JSON , XML , CBOR . You can also implement your own custom serializer.
JSON serializer To register the JSON serializer in your application, call the json
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
install(ContentNegotiation) {
json()
}
The json
method also allows you to adjust serialization settings provided by JsonBuilder , for example:
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
To register the Gson serializer in your application, call the gson
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.gson.*
install(ContentNegotiation) {
gson()
}
The gson
method also allows you to adjust serialization settings provided by GsonBuilder , for example:
install(ContentNegotiation) {
gson {
registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
setDateFormat(DateFormat.LONG, DateFormat.SHORT)
setPrettyPrinting()
}
To register the Jackson serializer in your application, call the jackson
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*
install(ContentNegotiation) {
jackson()
}
The jackson
method also allows you to adjust serialization settings provided by ObjectMapper , for example:
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
registerModule(JavaTimeModule()) // support java.time.* types
}
}
XML serializer To register the XML serializer in your application, call the xml
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
install(ContentNegotiation) {
xml()
}
The xml
method also allows you to access XML serialization settings, for example:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
import nl.adaptivity.xmlutil.*
import nl.adaptivity.xmlutil.serialization.*
install(ContentNegotiation) {
xml(format = XML {
xmlDeclMode = XmlDeclMode.Charset
})
}
CBOR serializer To register the CBOR serializer in your application, call the cbor
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
install(ContentNegotiation) {
cbor()
}
The cbor
method also allows you to access CBOR serialization settings provided by CborBuilder , for example:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
import kotlinx.serialization.cbor.*
install(ContentNegotiation) {
cbor(Cbor {
ignoreUnknownKeys = true
})
}
ProtoBuf serializer To register the ProtoBuf serializer in your application, call the protobuf
method:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
install(ContentNegotiation) {
protobuf()
}
The protobuf
method also allows you to access ProtoBuf serialization settings provided by ProtoBufBuilder , for example:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
import kotlinx.serialization.protobuf.*
install(ContentNegotiation) {
protobuf(ProtoBuf {
encodeDefaults = true
})
}
Custom serializer To register a custom serializer for a specified Content-Type
, you need to call the register
method. In the example below, two custom serializer are registered to deserialize application/json
and application/xml
data:
install(ContentNegotiation) {
register(ContentType.Application.Json, CustomJsonConverter())
register(ContentType.Application.Xml, CustomXmlConverter())
}
Receive and send data Create a data class To deserialize received data into an object, you need to create a data class, for example:
data class Customer(val id: Int, val firstName: String, val lastName: String)
If you use kotlinx.serialization, make sure that this class has the @Serializable
annotation:
import kotlinx.serialization.*
@Serializable
data class Customer(val id: Int, val firstName: String, val lastName: String)
Serializing/deserializing of the following types is supported by the kotlinx.serialization library:
Receive data To receive and convert a content for a request, call the receive
method that accepts a data class as a parameter:
post("/customer") {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
}
The Content-Type
of the request will be used to choose a serializer for processing the request. The example below shows a sample HTTP client request containing JSON or XML data that is converted to a Customer
object on the server side:
POST http://0.0.0.0:8080/customer
Content-Type: application/json
{
"id": 3,
"firstName": "Jet",
"lastName": "Brains"
}
POST http://0.0.0.0:8080/customer
Content-Type: application/xml
<Customer id="3" firstName="Jet" lastName="Brains"/>
You can find the full example here: json-kotlinx .
Send data To pass a data object in a response, you can use the respond
method:
get("/customer/{id}") {
val id = call.parameters["id"]
val customer: Customer = customerStorage.find { it.id == id!!.toInt() }!!
call.respond(customer)
}
In this case, Ktor uses the Accept
header to choose the required serializer . You can find the full example here: json-kotlinx .
Implement a custom serializer In Ktor, you can write your own serializer for serializing/deserializing data. To do this, you need to implement the ContentConverter interface:
interface ContentConverter {
suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent?
suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any?
}
Take a look at the GsonConverter class as an implementation example.
Last modified: 06 February 2025