Content Negotiation and Serialization
The ContentNegotiation feature serves two primary purposes:
Negotiating media types between the client and server. For this, it uses the
Accept
andContent-Type
headers.Serializing/deserializing the content in the specific format, which is provided by either the built-in
kotlinx.serialization
library or external ones, such asGson
andJackson
, amongst others.
Install ContentNegotiation
To install the ContentNegotiation
feature, pass it to the install
function in the application initialization code. This can be the main
function …
import io.ktor.features.*
// ...
fun Application.main() {
install(ContentNegotiation)
// ...
}
… or a specified module:
import io.ktor.features.*
// ...
fun Application.module() {
install(ContentNegotiation)
// ...
}
Register a Converter
To register a converter for a specified Content-Type
, you need to call the register method. In the example below, two custom converters are registered to deserialize application/json
and application/xml
data:
install(ContentNegotiation) {
register(ContentType.Application.Json, CustomJsonConverter())
register(ContentType.Application.Xml, CustomXmlConverter())
}
Built-in Converters
Ktor provides the set of built-in converters for handing various content types without writing your own logic:
Gson for JSON
Jackson for JSON
kotlinx.serialization for JSON, Protobuf, CBOR, and so on
See a corresponding topic to learn how to install the required dependencies, register, and configure a converter.
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
@Serializable
data class Customer(val id: Int, val firstName: String, val lastName: String)
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>()
}
The Content-Type
of the request will be used to choose a converter for processing the request. The example below shows a sample HTTP client request containing JSON data that will be converted to a Customer
object on the server side:
POST http://0.0.0.0:8080/customer
Content-Type: application/json
{
"id": 1,
"firstName" : "Jet",
"lastName": "Brains"
}
Send Data
To pass a data object in a response, you can use the respond method:
post("/customer") {
call.respond(Customer(1, "Jet", "Brains"))
}
In this case, Ktor uses the Accept
header to choose the required converter.
Implement a Custom Converter
In Ktor, you can write your own converter for serializing/deserializing data. To do this, you need to implement the ContentConverter interface:
interface ContentConverter {
suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
}
Take a look at the GsonConverter class as an implementation example.