JSON serialization plugin for Ktor HTTP client (JVM platform)
—
Jackson-based JSON serialization implementation for Ktor HTTP clients. Provides high-performance JSON processing with extensive configuration options, custom serializers, and advanced data binding features through the Jackson ObjectMapper.
JsonSerializer implementation using Jackson's ObjectMapper for JSON processing.
/**
* JsonSerializer using Jackson as backend.
*/
@Deprecated("Please use ContentNegotiation plugin and its converters")
class JacksonSerializer(
jackson: ObjectMapper = jacksonObjectMapper(),
block: ObjectMapper.() -> Unit = {}
) : JsonSerializer {
/**
* Convert data object to OutgoingContent using Jackson serialization.
*/
override fun write(data: Any, contentType: ContentType): OutgoingContent
/**
* Read content from response using Jackson deserialization.
*/
override fun read(type: TypeInfo, body: Input): Any
}The JacksonSerializer is automatically discoverable on JVM through the ServiceLoader mechanism:
META-INF/services/io.ktor.client.plugins.json.JsonSerializer
io.ktor.client.plugins.jackson.JacksonSerializerTo use JacksonSerializer, add the Jackson dependency to your project:
Gradle
implementation("io.ktor:ktor-client-jackson:2.3.13")Maven
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-jackson-jvm</artifactId>
<version>2.3.13</version>
</dependency>import io.ktor.client.*
import io.ktor.client.plugins.json.*
import io.ktor.client.plugins.jackson.*
val client = HttpClient {
install(JsonPlugin) {
serializer = JacksonSerializer()
}
}val client = HttpClient {
install(JsonPlugin) {
serializer = JacksonSerializer {
// Configure ObjectMapper
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.INDENT_OUTPUT, true)
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
// Property naming strategy
propertyNamingStrategy = PropertyNamingStrategies.SNAKE_CASE
// Date format
setDateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
// Null value handling
setSerializationInclusion(JsonInclude.Include.NON_NULL)
// Custom modules
registerModule(JavaTimeModule())
registerModule(KotlinModule.Builder().build())
}
}
}import com.fasterxml.jackson.annotation.*
import java.time.LocalDateTime
data class User(
val id: Long,
val name: String,
val email: String,
@JsonProperty("created_at")
val createdAt: LocalDateTime,
@JsonIgnore
val password: String? = null
)
// POST request - automatic serialization
val response = client.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(User(0, "Alice", "alice@example.com", LocalDateTime.now()))
}
// GET request - automatic deserialization
val user: User = client.get("https://api.example.com/users/1").body()import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.databind.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class LocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
private val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
override fun serialize(
value: LocalDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) {
gen.writeString(value.format(formatter))
}
}
class LocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
private val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
override fun deserialize(
parser: JsonParser,
context: DeserializationContext
): LocalDateTime {
return LocalDateTime.parse(parser.valueAsString, formatter)
}
}
// Usage with custom serializers
val client = HttpClient {
install(JsonPlugin) {
serializer = JacksonSerializer {
val module = SimpleModule()
module.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer())
module.addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer())
registerModule(module)
}
}
}import com.fasterxml.jackson.annotation.*
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(
JsonSubTypes.Type(value = Dog::class, name = "dog"),
JsonSubTypes.Type(value = Cat::class, name = "cat")
)
abstract class Animal(
open val name: String
)
@JsonTypeName("dog")
data class Dog(
override val name: String,
val breed: String
) : Animal(name)
@JsonTypeName("cat")
data class Cat(
override val name: String,
val color: String
) : Animal(name)
// Usage
val animals = listOf(
Dog("Buddy", "Golden Retriever"),
Cat("Whiskers", "Orange")
)
val response = client.post("https://api.example.com/animals") {
contentType(ContentType.Application.Json)
setBody(animals)
}// Generic type handling with TypeReference
import com.fasterxml.jackson.core.type.TypeReference
// List deserialization
val userListType = object : TypeReference<List<User>>() {}
val users: List<User> = client.get("https://api.example.com/users").body()
// Map deserialization
val userMapType = object : TypeReference<Map<String, User>>() {}
val userMap: Map<String, User> = client.get("https://api.example.com/users/map").body()
// Complex nested structures
data class ApiResponse<T>(
val data: T,
val status: String,
val message: String?
)
val responseType = object : TypeReference<ApiResponse<List<User>>>() {}
val response: ApiResponse<List<User>> = client.get("https://api.example.com/users").body()import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.databind.*
try {
val user: User = client.get("https://api.example.com/users/1").body()
} catch (e: JsonParseException) {
// Malformed JSON
logger.error("Invalid JSON response", e)
} catch (e: JsonMappingException) {
// Mapping error (type mismatch, missing properties)
logger.error("JSON mapping error", e)
} catch (e: JsonProcessingException) {
// General processing error
logger.error("JSON processing error", e)
}val client = HttpClient {
install(JsonPlugin) {
serializer = JacksonSerializer {
// Deserialization features
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false)
configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
// Serialization features
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false)
configure(SerializationFeature.INDENT_OUTPUT, true)
// Property inclusion
setSerializationInclusion(JsonInclude.Include.NON_NULL)
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
// Visibility settings
setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
// Property naming strategy
propertyNamingStrategy = PropertyNamingStrategies.SNAKE_CASE
// Date and time handling
registerModule(JavaTimeModule())
setDateFormat(SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"))
// Kotlin support
registerModule(KotlinModule.Builder()
.withReflectionCacheSize(512)
.configure(KotlinFeature.NullToEmptyCollection, false)
.configure(KotlinFeature.NullToEmptyMap, false)
.configure(KotlinFeature.SingletonSupport, true)
.build())
}
}
}// Using MixIn to add annotations to third-party classes
interface UserMixIn {
@JsonProperty("user_id")
fun getId(): Long
@JsonIgnore
fun getPassword(): String?
}
val client = HttpClient {
install(JsonPlugin) {
serializer = JacksonSerializer {
addMixIn(User::class.java, UserMixIn::class.java)
}
}
}The JacksonSerializer supports extensive ObjectMapper configuration:
FAIL_ON_UNKNOWN_PROPERTIES: Control unknown property handlingFAIL_ON_NULL_FOR_PRIMITIVES: Handle null values for primitivesACCEPT_EMPTY_STRING_AS_NULL_OBJECT: Empty string handlingREAD_UNKNOWN_ENUM_VALUES_AS_NULL: Enum value handlingFAIL_ON_EMPTY_BEANS: Empty object handlingWRITE_DATES_AS_TIMESTAMPS: Date format controlINDENT_OUTPUT: Pretty printingWRITE_NULL_MAP_VALUES: Null value inclusionJsonInclude.Include.NON_NULL: Exclude null valuesJsonInclude.Include.NON_EMPTY: Exclude empty collectionsJsonInclude.Include.NON_DEFAULT: Exclude default values@JsonIgnoreProperties(ignoreUnknown = true) for resilient deserializationInstall with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-json-jvm