Ktor JSON Content Negotiation via kotlinx.serialization support
npx @tessl/cli install tessl/maven-io-ktor--ktor-serialization-kotlinx-json@3.2.0Ktor JSON Content Negotiation via kotlinx.serialization support. This library provides JSON serialization and deserialization capabilities for Ktor applications using kotlinx.serialization, enabling automatic conversion between Kotlin objects and JSON format through the Content Negotiation plugin.
implementation("io.ktor:ktor-serialization-kotlinx-json:3.2.0")import io.ktor.serialization.kotlinx.json.*For kotlinx.serialization JSON configuration:
import kotlinx.serialization.json.JsonFor Content Negotiation setup:
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.server.plugins.contentnegotiation.*For exception handling:
import io.ktor.serialization.JsonConvertException
import io.ktor.serialization.ContentConvertExceptionFor extension provider:
import io.ktor.serialization.kotlinx.KotlinxSerializationExtensionProviderFor experimental converter:
import kotlinx.serialization.ExperimentalSerializationApiimport io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
fun Application.configureSerialization() {
install(ContentNegotiation) {
json() // Uses DefaultJson configuration
}
}import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
val client = HttpClient {
install(ContentNegotiation) {
json() // Uses DefaultJson configuration
}
}import kotlinx.serialization.json.Json
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
encodeDefaults = false
})
}Registers JSON content type with Content Negotiation plugin using kotlinx.serialization.
fun Configuration.json(
json: Json = DefaultJson,
contentType: ContentType = ContentType.Application.Json
)Parameters:
json: JSON format instance with configuration settings (optional, defaults to DefaultJson)contentType: Content type to register with the converter (optional, defaults to application/json)Registers JSON content type using experimental streaming JSON support for improved performance with large data.
@ExperimentalSerializationApi
fun Configuration.jsonIo(
json: Json = DefaultJson,
contentType: ContentType = ContentType.Application.Json
)Parameters:
json: JSON format instance (optional, defaults to DefaultJson)contentType: Content type to register (optional, defaults to application/json)Note: This uses experimental kotlinx-io streaming for better memory efficiency with large JSON data.
Special handling for streaming collections using JSON arrays.
class KotlinxSerializationJsonExtensionProvider : KotlinxSerializationExtensionProvider {
override fun extension(format: SerialFormat): KotlinxSerializationExtension?
}Features:
Flow<T> as JSON arrays with streamingSequence<T> (JVM only)Direct converter class for advanced use cases requiring custom content negotiation handling.
@ExperimentalSerializationApi
class ExperimentalJsonConverter(private val format: Json) : ContentConverter {
override suspend fun serialize(
contentType: ContentType,
charset: Charset,
typeInfo: TypeInfo,
value: Any?
): OutgoingContent
override suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: ByteReadChannel
): Any?
}Usage Example:
val converter = ExperimentalJsonConverter(Json {
prettyPrint = true
})
install(ContentNegotiation) {
register(ContentType.Application.Json, converter)
}Internal implementation class that provides the actual Flow/Sequence serialization functionality.
internal class KotlinxSerializationJsonExtensions(private val format: Json) : KotlinxSerializationExtension {
override suspend fun serialize(
contentType: ContentType,
charset: Charset,
typeInfo: TypeInfo,
value: Any?
): OutgoingContent?
override suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: ByteReadChannel
): Any?
}Note: This is an internal implementation class created by KotlinxSerializationJsonExtensionProvider and should not be used directly.
Pre-configured JSON instance optimized for Ktor applications.
val DefaultJson: JsonConfiguration Settings:
encodeDefaults = true - Serializes properties with default valuesisLenient = true - Allows lenient JSON parsingallowSpecialFloatingPointValues = true - Allows NaN, Infinity, -InfinityallowStructuredMapKeys = true - Allows complex objects as map keysprettyPrint = false - Compact JSON output for efficiencyuseArrayPolymorphism = false - Uses object-based polymorphism// From io.ktor.http package
ContentType.Application.Json // Standard application/json content type// Core serialization imports
import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.*
import io.ktor.utils.io.charsets.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.coroutines.flow.*// From io.ktor.serialization package
open class ContentConvertException(
message: String,
cause: Throwable? = null
) : Exception
class JsonConvertException(
message: String,
cause: Throwable? = null
) : ContentConvertExceptionContentConvertException: Base exception for content conversion errorsJsonConvertException: Thrown when JSON conversion fails during serialization or deserialization// From io.ktor.serialization.kotlinx package
interface KotlinxSerializationExtensionProvider {
fun extension(format: SerialFormat): KotlinxSerializationExtension?
}
interface KotlinxSerializationExtension {
suspend fun serialize(
contentType: ContentType,
charset: Charset,
typeInfo: TypeInfo,
value: Any?
): OutgoingContent?
suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: ByteReadChannel
): Any?
}
// From io.ktor.serialization package
interface ContentConverter {
suspend fun serialize(
contentType: ContentType,
charset: Charset,
typeInfo: TypeInfo,
value: Any?
): OutgoingContent
suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: ByteReadChannel
): Any?
}Sequence<T> deserializationJson.decodeToSequence(InputStream, KSerializer) for efficient sequence parsingInputStream for sequence parsing@Serializable
data class User(val id: Int, val name: String, val email: String)
// Server endpoint
post("/users") {
val user = call.receive<User>()
// Process user...
call.respond(user)
}
// Client request
val user = User(1, "John Doe", "john@example.com")
val response: User = client.post("http://localhost:8080/users") {
contentType(ContentType.Application.Json)
setBody(user)
}.body()// Server streaming response
get("/users/stream") {
val usersFlow: Flow<User> = getUsersAsFlow()
call.respond(usersFlow) // Automatically serialized as JSON array
}
// Client receiving stream (JVM only)
val users: Sequence<User> = client.get("http://localhost:8080/users/stream").body()
users.forEach { user ->
println("Received: $user")
}install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
coerceInputValues = true
useAlternativeNames = false
namingStrategy = JsonNamingStrategy.SnakeCase
})
}install(ContentNegotiation) {
json(DefaultJson, ContentType.Application.Json)
json(Json { prettyPrint = true }, ContentType("application", "vnd.api+json"))
}JsonConvertException: JSON parsing or serialization errorsSerializationException: kotlinx.serialization errors (missing serializers, etc.)ContentConvertException: Base content conversion errorstry {
val data = call.receive<MyDataClass>()
} catch (e: JsonConvertException) {
call.respond(HttpStatusCode.BadRequest, "Invalid JSON: ${e.message}")
} catch (e: SerializationException) {
call.respond(HttpStatusCode.BadRequest, "Serialization error: ${e.message}")
} catch (e: ContentConvertException) {
call.respond(HttpStatusCode.BadRequest, "Content conversion error: ${e.message}")
}