Kotlin multiplatform JSON serialization library with JavaScript-specific dynamic object conversion capabilities
—
JSON-specific annotations for controlling serialization behavior, providing alternative names, class discriminators, and selective ignoring of unknown properties.
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"}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"}
// ]
// }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 JsonIgnoreUnknownKeysUsage 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 strictThese 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
}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.
Class-level vs Global: @JsonIgnoreUnknownKeys on a class overrides the global ignoreUnknownKeys setting.
Hierarchy Inheritance: @JsonClassDiscriminator is inherited by subclasses automatically.
@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@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 fieldsInstall with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-json-js