Content negotiation and serialization in Ktor Client
The ContentNegotiation plugin serves two primary purposes:
Negotiating media types between the client and server, using the Accept and Content-Type headers.
Serializing request bodies and deserializing response bodies in supported formats. Ktor provides built-in support for JSON, XML, CBOR, and ProtoBuf.
On the server, Ktor provides the ContentNegotiation plugin for serializing and deserializing content.
Add dependencies Content negotiation To use ContentNegotiation, add the ktor-client-content-negotiation artifact to your build script:
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-content-negotiation-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
Serializers for specific formats require additional artifacts.
For example, kotlinx.serialization requires the ktor-serialization-kotlinx-json dependency for JSON. Depending on the included artifacts, Ktor chooses a default serializer automatically. If required, you can specify the serializer explicitly and configure it.
Serialization Before using kotlinx.serialization converters, add the Kotlin serialization plugin as described in the Setup section.
JSON To serialize and deserialize JSON data, add a serialization library to your project. Ktor supports kotlinx.serialization, Gson, or Jackson.
Add the ktor-serialization-kotlinx-json artifact in your 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 your 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 your 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 and deserialize XML, add the ktor-serialization-kotlinx-xml artifact in your 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 and deserialize CBOR, add the ktor-serialization-kotlinx-cbor artifact in your 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 and deserialize ProtoBuf, add the ktor-serialization-kotlinx-protobuf artifact in your 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, pass it to the install function in the client configuration block :
val client = HttpClient(CIO) {
install(ContentNegotiation)
}
You can then configure the required JSON serializer.
JSON serializer To register the JSON serializer in your application, call the json() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}
To customize JSON serialization, pass a Json configuration in the json() constructor:
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
}
For available configuration options, see JsonBuilder .
To register the Gson serializer in your application, call the gson() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.gson.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
gson()
}
}
To customize Gson serialization, pass a configuration block to the gson() function. For available configuration options, see GsonBuilder .
To register the Jackson serializer in your application, call the jackson() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
jackson()
}
}
To customize Jackson serialization, use the settings provided by ObjectMapper :
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*
import com.fasterxml.jackson.databind.*
import java.text.DateFormat
val client = HttpClient(CIO) {
install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
dateFormat = DateFormat.getDateInstance()
}
}
}
XML serializer To register the XML serializer in your application, call the xml() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
xml()
}
}
To customize XML serialization, pass the required options to the xml() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
import nl.adaptivity.xmlutil.*
import nl.adaptivity.xmlutil.serialization.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
xml(format = XML {
xmlDeclMode = XmlDeclMode.Charset
})
}
}
CBOR serializer To register the CBOR serializer in your application, call the cbor() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
cbor()
}
}
To customize CBOR serialization, pass a Cbor configuration in the cbor() constructor:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
import kotlinx.serialization.cbor.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
cbor(Cbor {
ignoreUnknownKeys = true
})
}
}
For available configuration options, see CborBuilder
ProtoBuf serializer To register the ProtoBuf serializer in your application, call the protobuf() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
protobuf()
}
}
To customize ProtoBuf serialization, pass a ProtoBuf configuration to the protobuf() function:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
import kotlinx.serialization.protobuf.*
val client = HttpClient(CIO) {
install(ContentNegotiation) {
protobuf(ProtoBuf {
encodeDefaults = true
})
}
}
For available options, see ProtoBufBuilder .
By default, the ContentNegotiation plugin adds registered content types to the Accept header of outgoing requests.
If you set an Accept header explicitly and don't want the plugin to add registered content types, set the acceptHeaderMergeStrategy property to ContentTypeMergeStrategy.SkipIfPresent:
val client = HttpClient(CIO) {
install(ContentNegotiation) {
register(ContentType.Application.Json, noOpJsonConverter)
acceptHeaderMergeStrategy = ContentTypeMergeStrategy.SkipIfPresent
}
}
With SkipIfPresent, the plugin preserves an existing Accept header. If the request doesn't contain an Accept header, the plugin adds the registered content types as usual.
Send and receive data Create a data class The following examples use a Customer data class to represent the data sent and received by the client:
data class Customer(val id: Int, val firstName: String, val lastName: String)
If you use kotlinx.serialization, annotate the class with @Serializable:
@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:
Send data To send a class instance in a request body, assign this instance using the setBody() function and set the content type using the contentType() function.
The following example sends a Customer object as JSON:
val response: HttpResponse = client.post("http://localhost:8080/customer") {
contentType(ContentType.Application.Json)
setBody(Customer(3, "Jet", "Brains"))
}
The ContentNegotiation plugin uses the configured serializer to convert the request body to the specified format.
To send data in another registered format, specify the corresponding content type, such as ContentType.Application.Xml or ContentType.Application.Cbor.
Receive data When the server returns a response with a supported content type, the ContentNegotiation plugin can deserialize the response body into the expected type.
For example, to deserialize a JSON response into a Customer object, call the body() function:
val customer: Customer = client.get("http://localhost:8080/customer/3").body()
04 September 2026