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 and Content-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 as Gson and Jackson, 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 …

  1. import io.ktor.features.*
  2. // ...
  3. fun Application.main() {
  4. install(ContentNegotiation)
  5. // ...
  6. }

… or a specified module:

  1. import io.ktor.features.*
  2. // ...
  3. fun Application.module() {
  4. install(ContentNegotiation)
  5. // ...
  6. }

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:

  1. install(ContentNegotiation) {
  2. register(ContentType.Application.Json, CustomJsonConverter())
  3. register(ContentType.Application.Xml, CustomXmlConverter())
  4. }

Built-in Converters

Ktor provides the set of built-in converters for handing various content types without writing your own logic:

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:

  1. 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:

  1. import kotlinx.serialization.Serializable
  2. @Serializable
  3. 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:

  1. post("/customer") {
  2. val customer = call.receive<Customer>()
  3. }

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:

  1. POST http://0.0.0.0:8080/customer
  2. Content-Type: application/json
  3. {
  4. "id": 1,
  5. "firstName" : "Jet",
  6. "lastName": "Brains"
  7. }

Send Data

To pass a data object in a response, you can use the respond method:

  1. post("/customer") {
  2. call.respond(Customer(1, "Jet", "Brains"))
  3. }

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:

  1. interface ContentConverter {
  2. suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
  3. suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
  4. }

Take a look at the GsonConverter class as an implementation example.