CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-json

Kotlin multiplatform JSON serialization library with type-safe, reflectionless approach supporting all platforms

Pending
Overview
Eval results
Files

annotations.mddocs/

Annotations

Annotations for controlling JSON serialization behavior including alternative property names and polymorphic serialization.

Capabilities

JsonNames

Annotation for specifying alternative property names during deserialization.

/**
 * Indicates that the field can be represented in JSON with multiple possible alternative names
 * Json format recognizes this annotation and can decode data using any of the alternative names
 * Does not affect JSON encoding in any way
 * @param names Alternative names for the property during deserialization
 */
@Target(AnnotationTarget.PROPERTY)
annotation class JsonNames(vararg val names: String)

Usage Examples:

@Serializable
data class User(
    val id: Int,
    @JsonNames("user_name", "username", "login") 
    val name: String,
    @JsonNames("user_email", "email_address")
    val email: String,
    @JsonNames("is_active", "enabled")
    val active: Boolean = true
)

// All of these JSON inputs will deserialize successfully
val json1 = """{"id":1,"name":"Alice","email":"alice@example.com","active":true}"""
val json2 = """{"id":1,"user_name":"Alice","user_email":"alice@example.com","is_active":true}"""
val json3 = """{"id":1,"username":"Alice","email_address":"alice@example.com","enabled":true}"""
val json4 = """{"id":1,"login":"Alice","user_email":"alice@example.com","is_active":true}"""

val user1 = Json.decodeFromString<User>(json1)
val user2 = Json.decodeFromString<User>(json2) 
val user3 = Json.decodeFromString<User>(json3)
val user4 = Json.decodeFromString<User>(json4)
// All create the same User object

// But encoding always uses the original property name
val encoded = Json.encodeToString(user1)
// {"id":1,"name":"Alice","email":"alice@example.com","active":true}

// Legacy API compatibility
@Serializable  
data class ApiResponse(
    @JsonNames("success", "ok", "status")
    val isSuccess: Boolean,
    @JsonNames("msg", "message", "error_message")
    val responseMessage: String,
    @JsonNames("payload", "result", "response_data")
    val data: JsonElement?
)

// Handles multiple API versions
val v1Response = """{"success":true,"msg":"OK","payload":{"user_id":123}}"""
val v2Response = """{"ok":true,"message":"OK","result":{"user_id":123}}""" 
val v3Response = """{"status":true,"error_message":"OK","response_data":{"user_id":123}}"""

// All deserialize to same object
val response1 = Json.decodeFromString<ApiResponse>(v1Response)
val response2 = Json.decodeFromString<ApiResponse>(v2Response)
val response3 = Json.decodeFromString<ApiResponse>(v3Response)

JsonClassDiscriminator

Annotation for specifying custom class discriminator key for polymorphic serialization.

/**
 * Specifies key for class discriminator value used during polymorphic serialization
 * Provided key is used only for annotated class and its subclasses
 * This annotation is inheritable, so placing it on base class affects all subclasses
 * @param discriminator Custom discriminator property name
 */
@Target(AnnotationTarget.CLASS)
annotation class JsonClassDiscriminator(val discriminator: String)

Usage Examples:

@Serializable
@JsonClassDiscriminator("message_type")
abstract class BaseMessage {
    abstract val timestamp: Long
}

@Serializable  
data class TextMessage(
    override val timestamp: Long,
    val content: String
) : BaseMessage()

@Serializable
data class ImageMessage(
    override val timestamp: Long, 
    val imageUrl: String,
    val caption: String?
) : BaseMessage()

@Serializable
data class SystemMessage(
    override val timestamp: Long,
    val systemCode: String
) : BaseMessage()

// Configure polymorphic serialization
val json = Json {
    serializersModule = SerializersModule {
        polymorphic(BaseMessage::class) {
            subclass(TextMessage::class)
            subclass(ImageMessage::class)
            subclass(SystemMessage::class)
        }
    }
}

// Serialization uses custom discriminator
val textMessage: BaseMessage = TextMessage(System.currentTimeMillis(), "Hello World")
val encoded = json.encodeToString(textMessage)
// {"message_type":"TextMessage","timestamp":1634567890123,"content":"Hello World"}

val decoded = json.decodeFromString<BaseMessage>(encoded)

// Different discriminator for different hierarchies
@Serializable
@JsonClassDiscriminator("event_type")
sealed class Event {
    @Serializable
    data class UserLogin(val userId: String, val timestamp: Long) : Event()
    
    @Serializable
    data class UserLogout(val userId: String, val sessionDuration: Long) : Event()
}

@Serializable
@JsonClassDiscriminator("shape_kind") 
sealed class Shape {
    @Serializable
    data class Circle(val radius: Double) : Shape()
    
    @Serializable
    data class Rectangle(val width: Double, val height: Double) : Shape()
}

// Each hierarchy uses its own discriminator
val eventJson = Json {
    serializersModule = SerializersModule {
        polymorphic(Event::class) {
            subclass(Event.UserLogin::class)
            subclass(Event.UserLogout::class)
        }
        polymorphic(Shape::class) {
            subclass(Shape.Circle::class)
            subclass(Shape.Rectangle::class)
        }
    }
}

val login: Event = Event.UserLogin("user123", System.currentTimeMillis())
val circle: Shape = Shape.Circle(5.0)

val loginJson = eventJson.encodeToString(login)  
// {"event_type":"UserLogin","userId":"user123","timestamp":1634567890123}

val circleJson = eventJson.encodeToString(circle)
// {"shape_kind":"Circle","radius":5.0}

JsonIgnoreUnknownKeys

Annotation for allowing unknown properties in specific classes during deserialization.

/**
 * Specifies that encounters of unknown properties in input JSON should be ignored 
 * instead of throwing SerializationException for this specific class
 * Allows selective ignoring of unknown keys without affecting global Json configuration
 */
@Target(AnnotationTarget.CLASS)
annotation class JsonIgnoreUnknownKeys

Usage Examples:

@Serializable
@JsonIgnoreUnknownKeys
data class FlexibleConfig(
    val host: String,
    val port: Int,
    val ssl: Boolean = false
)

@Serializable
data class StrictConfig(
    val apiKey: String,
    val endpoint: String
)

// FlexibleConfig ignores unknown properties
val flexibleJson = """{"host":"localhost","port":8080,"ssl":true,"unknown_field":"ignored","extra":123}"""
val flexibleConfig = Json.decodeFromString<FlexibleConfig>(flexibleJson)
// Success: FlexibleConfig(host="localhost", port=8080, ssl=true)

// StrictConfig throws exception for unknown properties
val strictJson = """{"apiKey":"secret","endpoint":"https://api.example.com","unknown":"field"}"""
// Json.decodeFromString<StrictConfig>(strictJson) 
// Would throw SerializationException: Encountered an unknown key 'unknown'

// Selective flexibility in complex structures
@Serializable
data class ApiRequest(
    val method: String,
    val path: String,
    @JsonIgnoreUnknownKeys
    val headers: Headers,
    val body: RequestBody
)

@Serializable 
@JsonIgnoreUnknownKeys
data class Headers(
    val contentType: String,
    val authorization: String? = null
)

@Serializable
data class RequestBody(
    val data: JsonElement
)

val requestJson = """
{
    "method": "POST",
    "path": "/api/users",
    "headers": {
        "contentType": "application/json",
        "authorization": "Bearer token123",
        "x-custom-header": "ignored",
        "user-agent": "also ignored"
    },
    "body": {
        "data": {"name": "Alice", "email": "alice@example.com"}
    }
}
"""

val request = Json.decodeFromString<ApiRequest>(requestJson)
// Success: Headers ignores unknown properties, but RequestBody must match exactly

// Evolution-friendly data classes
@Serializable
@JsonIgnoreUnknownKeys
data class UserProfile(
    val id: String,
    val name: String,
    val email: String,
    val createdAt: Long,
    // Future versions might add more fields, but this version ignores them
)

// Can handle JSON from newer API versions
val futureJson = """
{
    "id": "user123",
    "name": "Alice",
    "email": "alice@example.com", 
    "createdAt": 1634567890123,
    "profilePicture": "https://example.com/pic.jpg",
    "preferences": {"theme": "dark"},
    "newFeature": true
}
"""

val profile = Json.decodeFromString<UserProfile>(futureJson)
// Success: Unknown fields are ignored

Combining Annotations

Using multiple annotations together for flexible JSON handling.

Usage Examples:

@Serializable
@JsonIgnoreUnknownKeys
@JsonClassDiscriminator("notification_type")
sealed class Notification {
    abstract val id: String
    abstract val timestamp: Long
}

@Serializable
data class EmailNotification(
    override val id: String,
    override val timestamp: Long,
    @JsonNames("email_address", "recipient", "to")
    val email: String,
    @JsonNames("email_subject", "title")
    val subject: String,
    @JsonNames("email_body", "content", "message")
    val body: String
) : Notification()

@Serializable
data class PushNotification(
    override val id: String,
    override val timestamp: Long,
    @JsonNames("device_token", "token", "device_id")
    val deviceToken: String,
    @JsonNames("push_title", "notification_title")
    val title: String,
    @JsonNames("push_body", "notification_body", "text")
    val body: String
) : Notification()

val notificationJson = Json {
    serializersModule = SerializersModule {
        polymorphic(Notification::class) {
            subclass(EmailNotification::class)
            subclass(PushNotification::class)
        }
    }
}

// Handles various input formats with unknown fields
val legacyEmailJson = """
{
    "notification_type": "EmailNotification",
    "id": "email-001",
    "timestamp": 1634567890123,
    "email_address": "user@example.com",
    "email_subject": "Welcome!",
    "email_body": "Welcome to our service",
    "legacy_field": "ignored",
    "internal_id": 12345
}
"""

val modernPushJson = """
{
    "notification_type": "PushNotification", 
    "id": "push-001",
    "timestamp": 1634567890123,
    "device_token": "abc123def456",
    "push_title": "New Message",
    "push_body": "You have a new message",
    "priority": "high",
    "ttl": 3600
}
"""

val emailNotification = notificationJson.decodeFromString<Notification>(legacyEmailJson)
val pushNotification = notificationJson.decodeFromString<Notification>(modernPushJson)
// Both deserialize successfully despite unknown fields and alternative names

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-json

docs

annotations.md

configuration.md

core-operations.md

custom-serializers.md

dsl-builders.md

index.md

json-elements.md

naming-strategies.md

platform-extensions.md

tile.json