CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Kotlin multiplatform JSON serialization library with JavaScript-specific dynamic object conversion capabilities

Pending
Overview
Eval results
Files

json-annotations.mddocs/

JSON Annotations

JSON-specific annotations for controlling serialization behavior, providing alternative names, class discriminators, and selective ignoring of unknown properties.

Capabilities

JsonNames Annotation

Allows multiple alternative names for a single property during JSON deserialization.

/**
 * Indicates that the field can be represented in JSON with multiple possible alternative names
 * Unlike SerialName annotation, does not affect JSON encoding in any way
 * @param names Alternative names for the property
 */
@SerialInfo
@Target(AnnotationTarget.PROPERTY)
@ExperimentalSerializationApi
annotation class JsonNames(vararg val names: String)

Usage Examples:

@Serializable
data class Project(
    @JsonNames("title", "project_name") 
    val name: String,
    @JsonNames("desc", "description")
    val summary: String
)

val json = Json { useAlternativeNames = true }

// All of these work for deserialization
val project1 = json.decodeFromString<Project>("""{"name":"kotlinx.serialization","summary":"Kotlin serialization"}""")
val project2 = json.decodeFromString<Project>("""{"title":"kotlinx.coroutines","desc":"Kotlin coroutines"}""") 
val project3 = json.decodeFromString<Project>("""{"project_name":"kotlinx.datetime","description":"Kotlin datetime"}""")

// But encoding always uses the primary name
val encoded = json.encodeToString(project1)
// Result: {"name":"kotlinx.serialization","summary":"Kotlin serialization"}

JsonClassDiscriminator Annotation

Specifies a custom key for class discriminator values in polymorphic serialization.

/**
 * Specifies key for class discriminator value used during polymorphic serialization
 * This annotation is inheritable, so it should be sufficient to place it on a base class of hierarchy
 * @param discriminator The key name to use for the class discriminator
 */
@InheritableSerialInfo
@Target(AnnotationTarget.CLASS)
@ExperimentalSerializationApi
annotation class JsonClassDiscriminator(val discriminator: String)

Usage Examples:

@Serializable
@JsonClassDiscriminator("message_type")
abstract class BaseMessage

@Serializable
class TextMessage(val text: String) : BaseMessage()

@Serializable  
class ImageMessage(val imageUrl: String, val caption: String?) : BaseMessage()

@Serializable
class ChatData(val messages: List<BaseMessage>)

val json = Json.Default
val chat = ChatData(listOf(
    TextMessage("Hello world"),
    ImageMessage("https://example.com/image.jpg", "A beautiful sunset")
))

val encoded = json.encodeToString(chat)
// Result uses "message_type" instead of default "type":
// {
//   "messages": [
//     {"message_type":"TextMessage","text":"Hello world"},
//     {"message_type":"ImageMessage","imageUrl":"https://example.com/image.jpg","caption":"A beautiful sunset"}
//   ]
// }

JsonIgnoreUnknownKeys Annotation

Allows specific classes to ignore unknown properties during deserialization while maintaining strict checking for other classes.

/**
 * Specifies whether encounters of unknown properties should be ignored for this class
 * Provides per-class control over unknown key handling
 */
@SerialInfo
@Target(AnnotationTarget.CLASS)
@ExperimentalSerializationApi
annotation class JsonIgnoreUnknownKeys

Usage Examples:

@Serializable
@JsonIgnoreUnknownKeys
class FlexibleConfig(
    val host: String,
    val port: Int
)

@Serializable
class StrictConfig(
    val apiKey: String,
    val timeout: Int
)

@Serializable
class AppConfig(
    val flexible: FlexibleConfig,
    val strict: StrictConfig
)

val json = Json.Default // ignoreUnknownKeys = false globally

// This works - FlexibleConfig ignores unknown "ssl" property
val config1 = json.decodeFromString<FlexibleConfig>("""
    {"host":"localhost","port":8080,"ssl":true,"region":"us-east-1"}
""")

// This fails - StrictConfig doesn't ignore unknown "retries" property
try {
    val config2 = json.decodeFromString<StrictConfig>("""
        {"apiKey":"secret","timeout":5000,"retries":3}
    """)
} catch (e: SerializationException) {
    println("Failed: ${e.message}") // Unknown key 'retries'
}

// Mixed usage in nested structures
val appConfig = json.decodeFromString<AppConfig>("""
    {
        "flexible": {"host":"api.example.com","port":443,"ssl":true},
        "strict": {"apiKey":"abc123","timeout":10000}
    }
""")
// Works because FlexibleConfig ignores "ssl" but StrictConfig is still strict

Configuration Integration

These annotations work in conjunction with global Json configuration settings:

val json = Json {
    useAlternativeNames = true  // Required for @JsonNames to work
    ignoreUnknownKeys = false   // @JsonIgnoreUnknownKeys provides per-class override
    classDiscriminator = "type" // @JsonClassDiscriminator provides per-hierarchy override
}

Annotation Priority Rules

  1. JsonNames vs SerialName: @SerialName has higher priority. If property A has @SerialName("foo") and property B has @JsonNames("foo"), the key "foo" will deserialize to property A.

  2. Class-level vs Global: @JsonIgnoreUnknownKeys on a class overrides the global ignoreUnknownKeys setting.

  3. Hierarchy Inheritance: @JsonClassDiscriminator is inherited by subclasses automatically.

Common Patterns

API Evolution Support

@Serializable
data class UserProfile(
    @JsonNames("user_name", "username", "login") 
    val name: String,
    @JsonNames("user_id", "id")
    val userId: Long,
    val email: String
)

// Supports multiple API versions
val v1Response = """{"user_name":"alice","user_id":123,"email":"alice@example.com"}"""
val v2Response = """{"username":"bob","id":456,"email":"bob@example.com"}"""
val v3Response = """{"name":"charlie","userId":789,"email":"charlie@example.com"}"""

// All parse successfully to the same data class

Flexible Data Models

@Serializable
@JsonIgnoreUnknownKeys
data class PartialUser(
    val name: String,
    val email: String?
)

// Can extract just the needed fields from complex API responses
val complexApiResponse = """
{
    "name": "Alice",
    "email": "alice@example.com", 
    "address": {"street": "123 Main St", "city": "Anytown"},
    "preferences": {"theme": "dark", "notifications": true},
    "metadata": {"created": "2023-01-01", "lastLogin": "2023-12-01"}
}
"""

val user = json.decodeFromString<PartialUser>(complexApiResponse)
// Successfully extracts just name and email, ignoring all other fields

Install with Tessl CLI

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

docs

builder-dsl.md

configuration.md

core-operations.md

custom-serializers.md

dynamic-conversion.md

index.md

json-annotations.md

json-element.md

tile.json