JSON serialization plugin for Ktor HTTP client (JVM platform)
—
Abstract interface defining the contract for JSON serialization and deserialization in Ktor HTTP clients. Implementations provide specific backends like Gson or Kotlinx Serialization.
Core interface that all JSON serializers must implement to provide serialization and deserialization functionality.
/**
* Client json serializer.
*/
@Deprecated("Please use ContentNegotiation plugin and its converters")
interface JsonSerializer {
/**
* Convert data object to OutgoingContent.
*/
fun write(data: Any, contentType: ContentType): OutgoingContent
/**
* Convert data object to OutgoingContent using default JSON content type.
*/
fun write(data: Any): OutgoingContent
/**
* Read content from response using information specified in type.
*/
fun read(type: TypeInfo, body: Input): Any
}Factory function for obtaining platform-specific default JSON serializer implementations.
/**
* Platform default serializer.
* Uses service loader on JVM.
* Consider adding one of the following dependencies:
* - ktor-client-gson
* - ktor-client-json
*/
expect fun defaultSerializer(): JsonSerializerInterface for matching JSON content types, including variants with +json suffix.
interface ContentTypeMatcher {
fun contains(contentType: ContentType): Boolean
}
internal class JsonContentTypeMatcher : ContentTypeMatcher {
override fun contains(contentType: ContentType): Boolean
}On JVM, the default serializer uses Java's ServiceLoader mechanism to discover available JsonSerializer implementations.
// JVM-specific implementation
actual fun defaultSerializer(): JsonSerializer {
val serializers = ServiceLoader.load(JsonSerializer::class.java).toList()
if (serializers.isEmpty()) {
error("""Fail to find serializer. Consider adding one of:
- ktor-client-gson
- ktor-client-json
- ktor-client-serialization""")
}
return serializers.maxByOrNull { it::javaClass.name }!!
}Serializer implementations register themselves via Java's ServiceLoader mechanism:
META-INF/services/io.ktor.client.plugins.json.JsonSerializer
io.ktor.client.plugins.gson.GsonSerializerimport io.ktor.client.plugins.json.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.core.*
class MyCustomSerializer : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent {
val jsonString = mySerializationLibrary.toJson(data)
return TextContent(jsonString, contentType)
}
override fun write(data: Any): OutgoingContent {
return write(data, ContentType.Application.Json)
}
override fun read(type: TypeInfo, body: Input): Any {
val jsonString = body.readText()
return mySerializationLibrary.fromJson(jsonString, type.reifiedType)
}
}val client = HttpClient {
install(JsonPlugin) {
// Uses platform default - discovered via ServiceLoader on JVM
serializer = defaultSerializer()
}
}// Built-in JSON content type matcher
val matcher = JsonContentTypeMatcher()
// Matches standard JSON
matcher.contains(ContentType.Application.Json) // true
// Matches JSON variants
matcher.contains(ContentType.parse("application/hal+json")) // true
matcher.contains(ContentType.parse("application/vnd.api+json")) // true
// Doesn't match non-JSON types
matcher.contains(ContentType.Text.Plain) // falsetry {
val data = serializer.read(typeInfo, inputStream)
} catch (e: Exception) {
// Handle serialization errors
logger.error("Failed to deserialize JSON", e)
}When implementing JsonSerializer:
write method should respect the provided ContentType parameterread method must use TypeInfo to deserialize to the correct typeInstall with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-json-jvm