Kotlin multiplatform JSON serialization library with JavaScript-specific dynamic object conversion capabilities
—
JavaScript-specific functionality for converting between Kotlin objects and native JavaScript objects with full type safety. These APIs enable seamless interoperability with JavaScript code while maintaining Kotlin's type system.
Convert Kotlin objects to JavaScript dynamic objects for seamless JavaScript interop.
/**
* Converts Kotlin data structures to plain JavaScript objects
* @param serializer Serialization strategy for type T
* @param value The Kotlin value to convert
* @return JavaScript dynamic object
*/
@ExperimentalSerializationApi
fun <T> Json.encodeToDynamic(serializer: SerializationStrategy<T>, value: T): dynamic
/**
* Converts Kotlin data structures to plain JavaScript objects using reified type
* @param value The Kotlin value to convert
* @return JavaScript dynamic object
*/
@ExperimentalSerializationApi
inline fun <reified T> Json.encodeToDynamic(value: T): dynamicUsage Examples:
@Serializable
data class UserProfile(
val name: String,
val age: Int,
val isActive: Boolean,
val scores: List<Double>
)
val json = Json.Default
val profile = UserProfile("Alice", 25, true, listOf(85.5, 92.0, 78.3))
// Convert to JavaScript object
val jsObject = json.encodeToDynamic(profile)
// Use in JavaScript interop
console.log(jsObject.name) // "Alice"
console.log(jsObject.age) // 25
console.log(jsObject.isActive) // true
console.log(jsObject.scores) // JavaScript Array [85.5, 92.0, 78.3]
// Can be passed to JavaScript functions expecting plain objects
someJsFunction(jsObject)Convert JavaScript objects to typed Kotlin objects with full validation.
/**
* Converts native JavaScript objects into Kotlin ones, verifying their types
* @param deserializer Deserialization strategy for type T
* @param dynamic JavaScript object to convert
* @return Typed Kotlin object
* @throws JsonDecodingException if dynamic object structure is invalid
*/
@ExperimentalSerializationApi
fun <T> Json.decodeFromDynamic(deserializer: DeserializationStrategy<T>, dynamic: dynamic): T
/**
* Converts native JavaScript objects into Kotlin ones using reified type
* @param dynamic JavaScript object to convert
* @return Typed Kotlin object
* @throws JsonDecodingException if dynamic object structure is invalid
*/
@ExperimentalSerializationApi
inline fun <reified T> Json.decodeFromDynamic(dynamic: dynamic): TUsage Examples:
// JavaScript object created in JS code
val jsData: dynamic = js("""{
name: "Bob",
age: 30,
isActive: false,
scores: [90.5, 88.0, 95.2]
}""")
// Convert to typed Kotlin object
val profile = json.decodeFromDynamic<UserProfile>(jsData)
println(profile.name) // "Bob"
println(profile.age) // 30
println(profile.isActive) // false
println(profile.scores) // [90.5, 88.0, 95.2]
// Works with objects received from JavaScript APIs
fun processApiResponse(response: dynamic) {
try {
val user = json.decodeFromDynamic<UserProfile>(response)
// Use typed object safely
updateUI(user)
} catch (e: JsonDecodingException) {
handleInvalidResponse(e)
}
}Dynamic conversion handles JavaScript type mappings automatically:
@Serializable
data class TypeExample(
val stringVal: String,
val intVal: Int,
val boolVal: Boolean,
val doubleVal: Double,
val listVal: List<String>,
val mapVal: Map<String, Int>
)
val kotlinObj = TypeExample(
stringVal = "hello",
intVal = 42,
boolVal = true,
doubleVal = 3.14,
listVal = listOf("a", "b", "c"),
mapVal = mapOf("x" to 1, "y" to 2)
)
val jsObj = json.encodeToDynamic(kotlinObj)
// JavaScript object with native JS types:
// {
// stringVal: "hello",
// intVal: 42,
// boolVal: true,
// doubleVal: 3.14,
// listVal: ["a", "b", "c"],
// mapVal: {x: 1, y: 2}
// }Dynamic conversion works recursively with nested structures:
@Serializable
data class Address(val street: String, val city: String)
@Serializable
data class Person(
val name: String,
val address: Address,
val contacts: List<String>
)
val person = Person(
name = "Charlie",
address = Address("123 Main St", "Anytown"),
contacts = listOf("email@example.com", "555-1234")
)
val jsObj = json.encodeToDynamic(person)
// Nested JavaScript object:
// {
// name: "Charlie",
// address: {
// street: "123 Main St",
// city: "Anytown"
// },
// contacts: ["email@example.com", "555-1234"]
// }
// Convert back to Kotlin
val restored = json.decodeFromDynamic<Person>(jsObj)JavaScript numbers have precision limitations that affect Long values:
@Serializable
data class DataWithLong(val id: Long, val value: String)
// Safe Long values (within JavaScript's MAX_SAFE_INTEGER)
val safeData = DataWithLong(9007199254740991L, "safe")
val safeJs = json.encodeToDynamic(safeData) // Works correctly
// Unsafe Long values (exceed JavaScript precision)
val unsafeData = DataWithLong(9007199254740992L, "unsafe")
// This may lose precision when converted to JavaScript number
// Solution: Use String for large Long values
@Serializable
data class SafeLongData(
@Serializable(with = LongAsStringSerializer::class)
val id: Long,
val value: String
)Map keys must be primitive types as they're converted to JavaScript object properties:
@Serializable
data class ValidMaps(
val stringKeys: Map<String, Int>, // ✓ Valid
val intKeys: Map<Int, String>, // ✓ Valid (converted to string keys)
val enumKeys: Map<Color, String> // ✓ Valid (enum names as keys)
)
@Serializable
enum class Color { RED, GREEN, BLUE }
// Invalid: Complex objects as keys
@Serializable
data class InvalidMap(
val complexKeys: Map<Person, String> // ✗ Invalid - will fail
)Dynamic conversion preserves JavaScript null/undefined semantics where appropriate:
@Serializable
data class NullableData(val name: String, val value: String?)
val withNull = NullableData("test", null)
val jsObj = json.encodeToDynamic(withNull)
// JavaScript: {name: "test", value: null}
val withoutValue = js("{name: 'test'}") // undefined value property
val restored = json.decodeFromDynamic<NullableData>(withoutValue)
// Results in NullableData("test", null)// Convert Kotlin object for JavaScript Promise resolution
suspend fun fetchUserData(userId: String): dynamic {
val userData = getUserFromDatabase(userId)
return json.encodeToDynamic(userData)
}
// Handle JavaScript API responses
fun handleApiResponse(response: dynamic) {
val user = json.decodeFromDynamic<User>(response.data)
processUser(user)
}// Convert data for DOM storage
fun saveToLocalStorage(key: String, data: UserPreferences) {
val jsData = json.encodeToDynamic(data)
localStorage.setItem(key, JSON.stringify(jsData))
}
// Load data from DOM storage
fun loadFromLocalStorage(key: String): UserPreferences? {
val item = localStorage.getItem(key) ?: return null
val jsData = JSON.parse(item)
return json.decodeFromDynamic<UserPreferences>(jsData)
}Dynamic conversion behavior respects Json configuration settings:
val lenientJson = Json {
isLenient = true
ignoreUnknownKeys = true
}
// Both approaches should produce equivalent results
val jsonString = """{"name":"Alice","age":25,"extra":"ignored"}"""
val fromString = lenientJson.decodeFromString<Person>(jsonString)
val jsObj = JSON.parse(jsonString)
val fromDynamic = lenientJson.decodeFromDynamic<Person>(jsObj)
// fromString == fromDynamic should be trueInstall with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-serialization-json-js