Kotlin multiplatform JSON serialization library with JavaScript-specific dynamic object conversion capabilities
—
Comprehensive configuration system for customizing JSON encoding/decoding behavior, naming strategies, and polymorphism handling. The JsonBuilder class provides extensive options to control how JSON serialization behaves.
Configure Json instances through the JsonBuilder class with extensive customization options.
/**
* Builder class for configuring Json instances
*/
class JsonBuilder {
/**
* Encode default property values (default: false)
*/
var encodeDefaults: Boolean
/**
* Explicitly encode/require null values (default: true)
*/
var explicitNulls: Boolean
/**
* Ignore unknown JSON properties during decoding (default: false)
*/
var ignoreUnknownKeys: Boolean
/**
* Coerce invalid input values to defaults (default: false)
*/
var coerceInputValues: Boolean
/**
* Enable pretty-printed JSON output (default: false)
*/
var prettyPrint: Boolean
/**
* Indentation string for pretty printing (default: " ")
*/
var prettyPrintIndent: String
/**
* Allow malformed JSON (unquoted keys/values) (default: false)
*/
var isLenient: Boolean
/**
* Allow NaN, Infinity values (default: false)
*/
var allowSpecialFloatingPointValues: Boolean
/**
* Allow complex objects as map keys (default: false)
*/
var allowStructuredMapKeys: Boolean
/**
* Allow trailing commas in objects/arrays (default: false)
*/
var allowTrailingComma: Boolean
/**
* Allow C-style comments in JSON (default: false)
*/
var allowComments: Boolean
/**
* Use @JsonNames alternative names (default: true)
*/
var useAlternativeNames: Boolean
/**
* Global property name transformation (default: null)
*/
var namingStrategy: JsonNamingStrategy?
/**
* Case-insensitive enum parsing (default: false)
*/
var decodeEnumsCaseInsensitive: Boolean
/**
* Property name for type discriminator (default: "type")
*/
var classDiscriminator: String
/**
* When to add discriminators (default: POLYMORPHIC)
*/
var classDiscriminatorMode: ClassDiscriminatorMode
/**
* Use legacy array-based polymorphism (default: false)
*/
var useArrayPolymorphism: Boolean
}Control how objects are serialized and deserialized.
Usage Examples:
@Serializable
data class User(
val name: String,
val age: Int = 25, // Default value
val email: String? = null,
val isActive: Boolean = true
)
// Default behavior
val defaultJson = Json.Default
val user = User("Alice")
val defaultResult = defaultJson.encodeToString(user)
// Result: {"name":"Alice"}
// Note: Default values and nulls are omitted
// Include default values
val includeDefaultsJson = Json {
encodeDefaults = true
}
val withDefaults = includeDefaultsJson.encodeToString(user)
// Result: {"name":"Alice","age":25,"email":null,"isActive":true}
// Handle null values explicitly
val explicitNullsJson = Json {
encodeDefaults = true
explicitNulls = true
}
val userWithEmail = User("Bob", email = "bob@example.com")
val withExplicitNulls = explicitNullsJson.encodeToString(userWithEmail)
// Result: {"name":"Bob","age":25,"email":"bob@example.com","isActive":true}
val noExplicitNullsJson = Json {
encodeDefaults = true
explicitNulls = false
}
val withoutExplicitNulls = noExplicitNullsJson.encodeToString(userWithEmail)
// Result: {"name":"Bob","age":25,"email":"bob@example.com","isActive":true}
// Note: Non-null email is still includedHandle malformed or non-standard JSON input.
/**
* Configuration modes for handling malformed JSON
*/
enum class ClassDiscriminatorMode {
NONE, // Never add class discriminators
POLYMORPHIC, // Add discriminators for polymorphic classes only
ALL_JSON_OBJECTS // Add discriminators for all JSON objects
}Usage Examples:
// Strict JSON parsing (default)
val strictJson = Json.Default
// Lenient JSON parsing
val lenientJson = Json {
isLenient = true
ignoreUnknownKeys = true
coerceInputValues = true
allowSpecialFloatingPointValues = true
allowTrailingComma = true
allowComments = true
}
// Malformed JSON examples that work with lenient parsing
val malformedJson = """
{
// This is a comment
unquoted_key: "value",
"number": NaN,
"infinity": Infinity,
"trailing": "comma",
}
"""
try {
val parsed = lenientJson.decodeFromString<JsonElement>(malformedJson)
println("Lenient parsing succeeded")
} catch (e: JsonDecodingException) {
println("Lenient parsing failed: ${e.message}")
}
// Coercing invalid values
@Serializable
data class NumberData(val value: Int, val flag: Boolean)
val invalidDataJson = """{"value": "not_a_number", "flag": "yes"}"""
val coercingJson = Json {
coerceInputValues = true
}
val coercedData = coercingJson.decodeFromString<NumberData>(invalidDataJson)
// Result: NumberData(value = 0, flag = false) - invalid values coerced to defaultsFormat JSON output for human readability.
Usage Examples:
@Serializable
data class Product(
val id: String,
val name: String,
val categories: List<String>,
val metadata: Map<String, String>
)
val product = Product(
id = "PROD-001",
name = "Super Widget",
categories = listOf("electronics", "gadgets"),
metadata = mapOf("color" to "blue", "weight" to "1.5kg")
)
// Compact output (default)
val compactJson = Json.Default
val compact = compactJson.encodeToString(product)
// Result: {"id":"PROD-001","name":"Super Widget","categories":["electronics","gadgets"],"metadata":{"color":"blue","weight":"1.5kg"}}
// Pretty printed output
val prettyJson = Json {
prettyPrint = true
}
val pretty = prettyJson.encodeToString(product)
// Result:
// {
// "id": "PROD-001",
// "name": "Super Widget",
// "categories": [
// "electronics",
// "gadgets"
// ],
// "metadata": {
// "color": "blue",
// "weight": "1.5kg"
// }
// }
// Custom indentation
val customIndentJson = Json {
prettyPrint = true
prettyPrintIndent = " " // 2 spaces instead of 4
}
val customIndent = customIndentJson.encodeToString(product)Transform property names during serialization/deserialization.
/**
* Strategy for transforming property names
*/
interface JsonNamingStrategy {
/**
* Transform a serial name for JSON serialization
* @param descriptor Serial descriptor of the class
* @param elementIndex Index of the property in the class
* @param serialName Original property name
* @return Transformed property name for JSON
*/
fun serialNameForJson(descriptor: SerialDescriptor, elementIndex: Int, serialName: String): String
companion object {
/**
* Convert camelCase to snake_case
*/
val SnakeCase: JsonNamingStrategy
/**
* Convert camelCase to kebab-case
*/
val KebabCase: JsonNamingStrategy
}
}Usage Examples:
@Serializable
data class ApiResponse(
val userId: Int,
val firstName: String,
val lastName: String,
val emailAddress: String,
val isActiveUser: Boolean
)
val response = ApiResponse(
userId = 123,
firstName = "Alice",
lastName = "Smith",
emailAddress = "alice@example.com",
isActiveUser = true
)
// Default naming (camelCase)
val defaultNaming = Json.Default
val camelCase = defaultNaming.encodeToString(response)
// Result: {"userId":123,"firstName":"Alice","lastName":"Smith","emailAddress":"alice@example.com","isActiveUser":true}
// Snake case naming
val snakeCaseJson = Json {
namingStrategy = JsonNamingStrategy.SnakeCase
}
val snakeCase = snakeCaseJson.encodeToString(response)
// Result: {"user_id":123,"first_name":"Alice","last_name":"Smith","email_address":"alice@example.com","is_active_user":true}
// Kebab case naming
val kebabCaseJson = Json {
namingStrategy = JsonNamingStrategy.KebabCase
}
val kebabCase = kebabCaseJson.encodeToString(response)
// Result: {"user-id":123,"first-name":"Alice","last-name":"Smith","email-address":"alice@example.com","is-active-user":true}
// Custom naming strategy
val customNamingJson = Json {
namingStrategy = object : JsonNamingStrategy {
override fun serialNameForJson(descriptor: SerialDescriptor, elementIndex: Int, serialName: String): String {
return serialName.uppercase() // Convert all property names to uppercase
}
}
}
val uppercase = customNamingJson.encodeToString(response)
// Result: {"USERID":123,"FIRSTNAME":"Alice","LASTNAME":"Smith","EMAILADDRESS":"alice@example.com","ISACTIVEUSER":true}Support multiple property names during deserialization.
Usage Examples:
@Serializable
data class User(
@JsonNames("user_id", "id")
val userId: Int,
@JsonNames("user_name", "username", "login")
val name: String,
@JsonNames("email_address", "mail")
val email: String
)
val flexibleJson = Json {
useAlternativeNames = true // Default is true
}
// All these JSON variations can be deserialized to the same User object
val json1 = """{"userId":1,"name":"Alice","email":"alice@example.com"}"""
val json2 = """{"user_id":1,"user_name":"Alice","email_address":"alice@example.com"}"""
val json3 = """{"id":1,"username":"Alice","mail":"alice@example.com"}"""
val json4 = """{"user_id":1,"login":"Alice","email":"alice@example.com"}"""
val user1 = flexibleJson.decodeFromString<User>(json1)
val user2 = flexibleJson.decodeFromString<User>(json2)
val user3 = flexibleJson.decodeFromString<User>(json3)
val user4 = flexibleJson.decodeFromString<User>(json4)
// All result in the same User object
assert(user1 == user2)
assert(user2 == user3)
assert(user3 == user4)Configure enum serialization and deserialization behavior.
Usage Examples:
@Serializable
enum class Status { ACTIVE, INACTIVE, PENDING }
@Serializable
data class Record(val id: Int, val status: Status)
// Case-sensitive enum parsing (default)
val strictEnumJson = Json.Default
// Case-insensitive enum parsing
val lenientEnumJson = Json {
decodeEnumsCaseInsensitive = true
}
// These will work with case-insensitive parsing
val variations = listOf(
"""{"id":1,"status":"ACTIVE"}""",
"""{"id":1,"status":"active"}""",
"""{"id":1,"status":"Active"}""",
"""{"id":1,"status":"AcTiVe"}"""
)
variations.forEach { json ->
try {
val record = lenientEnumJson.decodeFromString<Record>(json)
println("Parsed: $record")
} catch (e: JsonDecodingException) {
println("Failed to parse: $json")
}
}Control polymorphic serialization behavior.
Usage Examples:
@Serializable
abstract class Animal {
abstract val name: String
}
@Serializable
@SerialName("dog")
data class Dog(override val name: String, val breed: String) : Animal()
@Serializable
@SerialName("cat")
data class Cat(override val name: String, val lives: Int) : Animal()
// Default polymorphic configuration
val defaultPolymorphicJson = Json {
classDiscriminator = "type" // Default
classDiscriminatorMode = ClassDiscriminatorMode.POLYMORPHIC // Default
}
val dog = Dog("Buddy", "Golden Retriever")
val polymorphicResult = defaultPolymorphicJson.encodeToString<Animal>(dog)
// Result: {"type":"dog","name":"Buddy","breed":"Golden Retriever"}
// Custom discriminator property name
val customDiscriminatorJson = Json {
classDiscriminator = "objectType"
}
val customResult = customDiscriminatorJson.encodeToString<Animal>(dog)
// Result: {"objectType":"dog","name":"Buddy","breed":"Golden Retriever"}
// Array-based polymorphism (legacy)
val arrayPolymorphicJson = Json {
useArrayPolymorphism = true
}
val arrayResult = arrayPolymorphicJson.encodeToString<Animal>(dog)
// Result: ["dog",{"name":"Buddy","breed":"Golden Retriever"}]
// Discriminator modes
val allObjectsJson = Json {
classDiscriminatorMode = ClassDiscriminatorMode.ALL_JSON_OBJECTS
classDiscriminator = "className"
}
@Serializable
data class SimpleData(val value: String)
val simpleData = SimpleData("test")
val withDiscriminator = allObjectsJson.encodeToString(simpleData)
// Result: {"className":"SimpleData","value":"test"}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-json-js