0
# JavaScript Dynamic Conversion
1
2
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.
3
4
## Capabilities
5
6
### Dynamic Encoding
7
8
Convert Kotlin objects to JavaScript dynamic objects for seamless JavaScript interop.
9
10
```kotlin { .api }
11
/**
12
* Converts Kotlin data structures to plain JavaScript objects
13
* @param serializer Serialization strategy for type T
14
* @param value The Kotlin value to convert
15
* @return JavaScript dynamic object
16
*/
17
@ExperimentalSerializationApi
18
fun <T> Json.encodeToDynamic(serializer: SerializationStrategy<T>, value: T): dynamic
19
20
/**
21
* Converts Kotlin data structures to plain JavaScript objects using reified type
22
* @param value The Kotlin value to convert
23
* @return JavaScript dynamic object
24
*/
25
@ExperimentalSerializationApi
26
inline fun <reified T> Json.encodeToDynamic(value: T): dynamic
27
```
28
29
**Usage Examples:**
30
31
```kotlin
32
@Serializable
33
data class UserProfile(
34
val name: String,
35
val age: Int,
36
val isActive: Boolean,
37
val scores: List<Double>
38
)
39
40
val json = Json.Default
41
val profile = UserProfile("Alice", 25, true, listOf(85.5, 92.0, 78.3))
42
43
// Convert to JavaScript object
44
val jsObject = json.encodeToDynamic(profile)
45
46
// Use in JavaScript interop
47
console.log(jsObject.name) // "Alice"
48
console.log(jsObject.age) // 25
49
console.log(jsObject.isActive) // true
50
console.log(jsObject.scores) // JavaScript Array [85.5, 92.0, 78.3]
51
52
// Can be passed to JavaScript functions expecting plain objects
53
someJsFunction(jsObject)
54
```
55
56
### Dynamic Decoding
57
58
Convert JavaScript objects to typed Kotlin objects with full validation.
59
60
```kotlin { .api }
61
/**
62
* Converts native JavaScript objects into Kotlin ones, verifying their types
63
* @param deserializer Deserialization strategy for type T
64
* @param dynamic JavaScript object to convert
65
* @return Typed Kotlin object
66
* @throws JsonDecodingException if dynamic object structure is invalid
67
*/
68
@ExperimentalSerializationApi
69
fun <T> Json.decodeFromDynamic(deserializer: DeserializationStrategy<T>, dynamic: dynamic): T
70
71
/**
72
* Converts native JavaScript objects into Kotlin ones using reified type
73
* @param dynamic JavaScript object to convert
74
* @return Typed Kotlin object
75
* @throws JsonDecodingException if dynamic object structure is invalid
76
*/
77
@ExperimentalSerializationApi
78
inline fun <reified T> Json.decodeFromDynamic(dynamic: dynamic): T
79
```
80
81
**Usage Examples:**
82
83
```kotlin
84
// JavaScript object created in JS code
85
val jsData: dynamic = js("""{
86
name: "Bob",
87
age: 30,
88
isActive: false,
89
scores: [90.5, 88.0, 95.2]
90
}""")
91
92
// Convert to typed Kotlin object
93
val profile = json.decodeFromDynamic<UserProfile>(jsData)
94
95
println(profile.name) // "Bob"
96
println(profile.age) // 30
97
println(profile.isActive) // false
98
println(profile.scores) // [90.5, 88.0, 95.2]
99
100
// Works with objects received from JavaScript APIs
101
fun processApiResponse(response: dynamic) {
102
try {
103
val user = json.decodeFromDynamic<UserProfile>(response)
104
// Use typed object safely
105
updateUI(user)
106
} catch (e: JsonDecodingException) {
107
handleInvalidResponse(e)
108
}
109
}
110
```
111
112
## JavaScript Interoperability
113
114
### Data Type Conversions
115
116
Dynamic conversion handles JavaScript type mappings automatically:
117
118
```kotlin
119
@Serializable
120
data class TypeExample(
121
val stringVal: String,
122
val intVal: Int,
123
val boolVal: Boolean,
124
val doubleVal: Double,
125
val listVal: List<String>,
126
val mapVal: Map<String, Int>
127
)
128
129
val kotlinObj = TypeExample(
130
stringVal = "hello",
131
intVal = 42,
132
boolVal = true,
133
doubleVal = 3.14,
134
listVal = listOf("a", "b", "c"),
135
mapVal = mapOf("x" to 1, "y" to 2)
136
)
137
138
val jsObj = json.encodeToDynamic(kotlinObj)
139
// JavaScript object with native JS types:
140
// {
141
// stringVal: "hello",
142
// intVal: 42,
143
// boolVal: true,
144
// doubleVal: 3.14,
145
// listVal: ["a", "b", "c"],
146
// mapVal: {x: 1, y: 2}
147
// }
148
```
149
150
### Nested Objects
151
152
Dynamic conversion works recursively with nested structures:
153
154
```kotlin
155
@Serializable
156
data class Address(val street: String, val city: String)
157
158
@Serializable
159
data class Person(
160
val name: String,
161
val address: Address,
162
val contacts: List<String>
163
)
164
165
val person = Person(
166
name = "Charlie",
167
address = Address("123 Main St", "Anytown"),
168
contacts = listOf("email@example.com", "555-1234")
169
)
170
171
val jsObj = json.encodeToDynamic(person)
172
// Nested JavaScript object:
173
// {
174
// name: "Charlie",
175
// address: {
176
// street: "123 Main St",
177
// city: "Anytown"
178
// },
179
// contacts: ["email@example.com", "555-1234"]
180
// }
181
182
// Convert back to Kotlin
183
val restored = json.decodeFromDynamic<Person>(jsObj)
184
```
185
186
## Limitations and Considerations
187
188
### Long Value Limitations
189
190
JavaScript numbers have precision limitations that affect Long values:
191
192
```kotlin
193
@Serializable
194
data class DataWithLong(val id: Long, val value: String)
195
196
// Safe Long values (within JavaScript's MAX_SAFE_INTEGER)
197
val safeData = DataWithLong(9007199254740991L, "safe")
198
val safeJs = json.encodeToDynamic(safeData) // Works correctly
199
200
// Unsafe Long values (exceed JavaScript precision)
201
val unsafeData = DataWithLong(9007199254740992L, "unsafe")
202
// This may lose precision when converted to JavaScript number
203
204
// Solution: Use String for large Long values
205
@Serializable
206
data class SafeLongData(
207
@Serializable(with = LongAsStringSerializer::class)
208
val id: Long,
209
val value: String
210
)
211
```
212
213
### Map Key Limitations
214
215
Map keys must be primitive types as they're converted to JavaScript object properties:
216
217
```kotlin
218
@Serializable
219
data class ValidMaps(
220
val stringKeys: Map<String, Int>, // ✓ Valid
221
val intKeys: Map<Int, String>, // ✓ Valid (converted to string keys)
222
val enumKeys: Map<Color, String> // ✓ Valid (enum names as keys)
223
)
224
225
@Serializable
226
enum class Color { RED, GREEN, BLUE }
227
228
// Invalid: Complex objects as keys
229
@Serializable
230
data class InvalidMap(
231
val complexKeys: Map<Person, String> // ✗ Invalid - will fail
232
)
233
```
234
235
### Null vs Undefined
236
237
Dynamic conversion preserves JavaScript null/undefined semantics where appropriate:
238
239
```kotlin
240
@Serializable
241
data class NullableData(val name: String, val value: String?)
242
243
val withNull = NullableData("test", null)
244
val jsObj = json.encodeToDynamic(withNull)
245
// JavaScript: {name: "test", value: null}
246
247
val withoutValue = js("{name: 'test'}") // undefined value property
248
val restored = json.decodeFromDynamic<NullableData>(withoutValue)
249
// Results in NullableData("test", null)
250
```
251
252
## Integration with JavaScript APIs
253
254
### Working with JavaScript Promises
255
256
```kotlin
257
// Convert Kotlin object for JavaScript Promise resolution
258
suspend fun fetchUserData(userId: String): dynamic {
259
val userData = getUserFromDatabase(userId)
260
return json.encodeToDynamic(userData)
261
}
262
263
// Handle JavaScript API responses
264
fun handleApiResponse(response: dynamic) {
265
val user = json.decodeFromDynamic<User>(response.data)
266
processUser(user)
267
}
268
```
269
270
### DOM Integration
271
272
```kotlin
273
// Convert data for DOM storage
274
fun saveToLocalStorage(key: String, data: UserPreferences) {
275
val jsData = json.encodeToDynamic(data)
276
localStorage.setItem(key, JSON.stringify(jsData))
277
}
278
279
// Load data from DOM storage
280
fun loadFromLocalStorage(key: String): UserPreferences? {
281
val item = localStorage.getItem(key) ?: return null
282
val jsData = JSON.parse(item)
283
return json.decodeFromDynamic<UserPreferences>(jsData)
284
}
285
```
286
287
### Configuration and Equivalence
288
289
Dynamic conversion behavior respects Json configuration settings:
290
291
```kotlin
292
val lenientJson = Json {
293
isLenient = true
294
ignoreUnknownKeys = true
295
}
296
297
// Both approaches should produce equivalent results
298
val jsonString = """{"name":"Alice","age":25,"extra":"ignored"}"""
299
val fromString = lenientJson.decodeFromString<Person>(jsonString)
300
301
val jsObj = JSON.parse(jsonString)
302
val fromDynamic = lenientJson.decodeFromDynamic<Person>(jsObj)
303
304
// fromString == fromDynamic should be true
305
```