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

dynamic-conversion.mddocs/

JavaScript Dynamic Conversion

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.

Capabilities

Dynamic Encoding

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): dynamic

Usage 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)

Dynamic Decoding

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): T

Usage 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)
    }
}

JavaScript Interoperability

Data Type Conversions

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}
// }

Nested Objects

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)

Limitations and Considerations

Long Value Limitations

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 Key Limitations

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
)

Null vs Undefined

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)

Integration with JavaScript APIs

Working with JavaScript Promises

// 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)
}

DOM Integration

// 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)
}

Configuration and Equivalence

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 true

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