0
# Platform-Specific APIs
1
2
Platform-specific extensions providing optimized JSON processing for JVM stream operations and JavaScript dynamic object interoperability.
3
4
## Capabilities
5
6
### JVM Stream Operations
7
8
JVM-specific extensions for working with InputStream and OutputStream, enabling efficient streaming JSON processing.
9
10
```kotlin { .api }
11
/**
12
* Serialize a value directly to an OutputStream
13
* @param serializer Serialization strategy for type T
14
* @param value Object to serialize
15
* @param stream OutputStream to write JSON to
16
*/
17
@ExperimentalSerializationApi
18
fun <T> Json.encodeToStream(
19
serializer: SerializationStrategy<T>,
20
value: T,
21
stream: OutputStream
22
)
23
24
/**
25
* Serialize a value directly to an OutputStream using reified type
26
* @param value Object to serialize
27
* @param stream OutputStream to write JSON to
28
*/
29
@ExperimentalSerializationApi
30
inline fun <reified T> Json.encodeToStream(value: T, stream: OutputStream)
31
32
/**
33
* Deserialize JSON from an InputStream
34
* @param deserializer Deserialization strategy for type T
35
* @param stream InputStream containing JSON data
36
* @return Deserialized object of type T
37
*/
38
@ExperimentalSerializationApi
39
fun <T> Json.decodeFromStream(
40
deserializer: DeserializationStrategy<T>,
41
stream: InputStream
42
): T
43
44
/**
45
* Deserialize JSON from an InputStream using reified type
46
* @param stream InputStream containing JSON data
47
* @return Deserialized object of type T
48
*/
49
@ExperimentalSerializationApi
50
inline fun <reified T> Json.decodeFromStream(stream: InputStream): T
51
52
/**
53
* Decode a sequence of JSON objects from an InputStream
54
* @param stream InputStream containing JSON sequence
55
* @param deserializer Deserialization strategy for type T
56
* @param format Format of the JSON sequence
57
* @return Sequence of deserialized objects
58
*/
59
@ExperimentalSerializationApi
60
fun <T> Json.decodeToSequence(
61
stream: InputStream,
62
deserializer: DeserializationStrategy<T>,
63
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
64
): Sequence<T>
65
66
/**
67
* Decode a sequence of JSON objects from an InputStream using reified type
68
* @param stream InputStream containing JSON sequence
69
* @param format Format of the JSON sequence
70
* @return Sequence of deserialized objects
71
*/
72
@ExperimentalSerializationApi
73
inline fun <reified T> Json.decodeToSequence(
74
stream: InputStream,
75
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
76
): Sequence<T>
77
78
/**
79
* Mode for decoding JSON sequences
80
*/
81
@ExperimentalSerializationApi
82
enum class DecodeSequenceMode {
83
/** Objects separated by whitespace: {"a":1} {"b":2} */
84
WHITESPACE_SEPARATED,
85
/** Objects wrapped in array: [{"a":1}, {"b":2}] */
86
ARRAY_WRAPPED,
87
/** Automatically detect the format */
88
AUTO_DETECT
89
}
90
```
91
92
**Usage Examples:**
93
94
```kotlin
95
import kotlinx.serialization.*
96
import kotlinx.serialization.json.*
97
import java.io.*
98
99
@Serializable
100
data class LogEntry(
101
val timestamp: Long,
102
val level: String,
103
val message: String,
104
val metadata: Map<String, String> = emptyMap()
105
)
106
107
// Writing to streams
108
val entries = listOf(
109
LogEntry(System.currentTimeMillis(), "INFO", "Application started"),
110
LogEntry(System.currentTimeMillis(), "DEBUG", "Configuration loaded"),
111
LogEntry(System.currentTimeMillis(), "ERROR", "Database connection failed")
112
)
113
114
// Encode single object to stream
115
ByteArrayOutputStream().use { output ->
116
Json.encodeToStream(entries, output)
117
val jsonBytes = output.toByteArray()
118
println(String(jsonBytes))
119
}
120
121
// Encode to file
122
FileOutputStream("logs.json").use { fileOutput ->
123
Json.encodeToStream(entries, fileOutput)
124
}
125
126
// Reading from streams
127
FileInputStream("logs.json").use { fileInput ->
128
val loadedEntries = Json.decodeFromStream<List<LogEntry>>(fileInput)
129
loadedEntries.forEach { println(it) }
130
}
131
132
// Process large JSON array as sequence to avoid loading all into memory
133
FileInputStream("large-logs.json").use { input ->
134
val sequence = Json.decodeToSequence<LogEntry>(input, DecodeSequenceMode.ARRAY_WRAPPED)
135
sequence
136
.filter { it.level == "ERROR" }
137
.take(100)
138
.forEach { println("Error: ${it.message}") }
139
}
140
```
141
142
**Streaming JSON Sequences:**
143
144
```kotlin
145
import kotlinx.serialization.*
146
import kotlinx.serialization.json.*
147
import java.io.*
148
149
@Serializable
150
data class SensorReading(
151
val sensorId: String,
152
val timestamp: Long,
153
val temperature: Double,
154
val humidity: Double
155
)
156
157
// Reading whitespace-separated JSON objects
158
val whitespaceSeparatedJson = """
159
{"sensorId":"temp001","timestamp":1234567890,"temperature":22.5,"humidity":45.0}
160
{"sensorId":"temp002","timestamp":1234567891,"temperature":23.1,"humidity":46.2}
161
{"sensorId":"temp001","timestamp":1234567892,"temperature":22.8,"humidity":44.8}
162
"""
163
164
ByteArrayInputStream(whitespaceSeparatedJson.toByteArray()).use { input ->
165
val readings = Json.decodeToSequence<SensorReading>(
166
input,
167
DecodeSequenceMode.WHITESPACE_SEPARATED
168
)
169
170
readings.forEach { reading ->
171
println("Sensor ${reading.sensorId}: ${reading.temperature}°C, ${reading.humidity}% humidity")
172
}
173
}
174
175
// Reading array-wrapped format
176
val arrayWrappedJson = """
177
[
178
{"sensorId":"temp001","timestamp":1234567890,"temperature":22.5,"humidity":45.0},
179
{"sensorId":"temp002","timestamp":1234567891,"temperature":23.1,"humidity":46.2}
180
]
181
"""
182
183
ByteArrayInputStream(arrayWrappedJson.toByteArray()).use { input ->
184
val readings = Json.decodeToSequence<SensorReading>(
185
input,
186
DecodeSequenceMode.ARRAY_WRAPPED
187
)
188
189
val avgTemp = readings.map { it.temperature }.average()
190
println("Average temperature: $avgTemp°C")
191
}
192
193
// Auto-detect format
194
ByteArrayInputStream(whitespaceSeparatedJson.toByteArray()).use { input ->
195
val readings = Json.decodeToSequence<SensorReading>(
196
input,
197
DecodeSequenceMode.AUTO_DETECT // Will detect whitespace-separated format
198
)
199
200
readings.forEach { println(it) }
201
}
202
```
203
204
### JavaScript Dynamic Object Interop
205
206
JavaScript-specific extensions for seamless interoperability with JavaScript's dynamic object system.
207
208
```kotlin { .api }
209
/**
210
* Deserialize from JavaScript dynamic object
211
* @param deserializer Deserialization strategy for type T
212
* @param dynamic JavaScript dynamic object
213
* @return Deserialized Kotlin object of type T
214
*/
215
@ExperimentalSerializationApi
216
fun <T> Json.decodeFromDynamic(
217
deserializer: DeserializationStrategy<T>,
218
dynamic: dynamic
219
): T
220
221
/**
222
* Deserialize from JavaScript dynamic object using reified type
223
* @param dynamic JavaScript dynamic object
224
* @return Deserialized Kotlin object of type T
225
*/
226
@ExperimentalSerializationApi
227
inline fun <reified T> Json.decodeFromDynamic(dynamic: dynamic): T
228
229
/**
230
* Serialize to JavaScript dynamic object
231
* @param serializer Serialization strategy for type T
232
* @param value Kotlin object to serialize
233
* @return JavaScript dynamic object
234
*/
235
@ExperimentalSerializationApi
236
fun <T> Json.encodeToDynamic(
237
serializer: SerializationStrategy<T>,
238
value: T
239
): dynamic
240
241
/**
242
* Serialize to JavaScript dynamic object using reified type
243
* @param value Kotlin object to serialize
244
* @return JavaScript dynamic object
245
*/
246
@ExperimentalSerializationApi
247
inline fun <reified T> Json.encodeToDynamic(value: T): dynamic
248
```
249
250
**Usage Examples:**
251
252
```kotlin
253
// Kotlin/JS specific code
254
import kotlinx.serialization.*
255
import kotlinx.serialization.json.*
256
257
@Serializable
258
data class UserProfile(
259
val id: Int,
260
val name: String,
261
val email: String,
262
val preferences: Map<String, String>
263
)
264
265
// Working with JavaScript objects in Kotlin/JS
266
external interface JsUserProfile {
267
val id: Int
268
val name: String
269
val email: String
270
val preferences: dynamic
271
}
272
273
// Convert from JavaScript object to Kotlin data class
274
fun processJavaScriptUser(jsUser: JsUserProfile): UserProfile {
275
// Convert JS object to dynamic, then to Kotlin object
276
return Json.decodeFromDynamic<UserProfile>(jsUser.asDynamic())
277
}
278
279
// Convert Kotlin object to JavaScript object
280
fun createJavaScriptUser(user: UserProfile): dynamic {
281
return Json.encodeToDynamic(user)
282
}
283
284
// Usage with browser APIs
285
fun saveUserToLocalStorage(user: UserProfile) {
286
val jsObject = Json.encodeToDynamic(user)
287
kotlinx.browser.localStorage.setItem("user", JSON.stringify(jsObject))
288
}
289
290
fun loadUserFromLocalStorage(): UserProfile? {
291
val userJson = kotlinx.browser.localStorage.getItem("user") ?: return null
292
val jsObject = JSON.parse<dynamic>(userJson)
293
return Json.decodeFromDynamic<UserProfile>(jsObject)
294
}
295
```
296
297
**Advanced JavaScript Interop:**
298
299
```kotlin
300
// Kotlin/JS specific code
301
import kotlinx.serialization.*
302
import kotlinx.serialization.json.*
303
304
@Serializable
305
data class ApiResponse<T>(
306
val success: Boolean,
307
val data: T?,
308
val error: String?,
309
val metadata: Map<String, String> = emptyMap()
310
)
311
312
@Serializable
313
data class Product(
314
val id: Int,
315
val name: String,
316
val price: Double,
317
val categories: List<String>
318
)
319
320
// Interop with JavaScript fetch API
321
suspend fun fetchProducts(): List<Product> {
322
val response = kotlinx.browser.window.fetch("/api/products").await()
323
val jsObject = response.json().await()
324
325
val apiResponse = Json.decodeFromDynamic<ApiResponse<List<Product>>>(jsObject)
326
327
return if (apiResponse.success) {
328
apiResponse.data ?: emptyList()
329
} else {
330
throw Exception(apiResponse.error ?: "Unknown error")
331
}
332
}
333
334
// Send data to JavaScript API
335
suspend fun createProduct(product: Product): Product {
336
val jsProduct = Json.encodeToDynamic(product)
337
338
val response = kotlinx.browser.window.fetch("/api/products", object {
339
val method = "POST"
340
val headers = js("{}")
341
val body = JSON.stringify(jsProduct)
342
}.asDynamic()).await()
343
344
val jsResult = response.json().await()
345
val apiResponse = Json.decodeFromDynamic<ApiResponse<Product>>(jsResult)
346
347
return if (apiResponse.success) {
348
apiResponse.data ?: throw Exception("No product data returned")
349
} else {
350
throw Exception(apiResponse.error ?: "Failed to create product")
351
}
352
}
353
354
// Working with complex nested JavaScript objects
355
external interface ComplexJsObject {
356
val users: Array<dynamic>
357
val settings: dynamic
358
val metadata: dynamic
359
}
360
361
fun processComplexObject(jsObj: ComplexJsObject) {
362
// Convert JavaScript arrays and objects to Kotlin types
363
val users = jsObj.users.map { userJs ->
364
Json.decodeFromDynamic<UserProfile>(userJs)
365
}
366
367
val settings = Json.decodeFromDynamic<Map<String, String>>(jsObj.settings)
368
369
val metadata = Json.decodeFromDynamic<Map<String, Any>>(jsObj.metadata)
370
371
// Process the converted data
372
users.forEach { user ->
373
println("Processing user: ${user.name}")
374
}
375
}
376
```
377
378
### Error Handling in Platform APIs
379
380
Platform-specific APIs can throw additional exceptions related to I/O operations.
381
382
```kotlin
383
import kotlinx.serialization.json.*
384
import java.io.*
385
386
fun safeStreamProcessing(inputFile: String, outputFile: String) {
387
try {
388
FileInputStream(inputFile).use { input ->
389
FileOutputStream(outputFile).use { output ->
390
val data = Json.decodeFromStream<List<String>>(input)
391
val processed = data.map { it.uppercase() }
392
Json.encodeToStream(processed, output)
393
}
394
}
395
} catch (e: IOException) {
396
println("File I/O error: ${e.message}")
397
} catch (e: SerializationException) {
398
println("JSON serialization error: ${e.message}")
399
} catch (e: Exception) {
400
println("Unexpected error: ${e.message}")
401
}
402
}
403
404
// JavaScript error handling
405
fun safeDynamicProcessing(jsObject: dynamic): UserProfile? {
406
return try {
407
Json.decodeFromDynamic<UserProfile>(jsObject)
408
} catch (e: SerializationException) {
409
console.log("Failed to decode JavaScript object: ${e.message}")
410
null
411
} catch (e: Exception) {
412
console.log("Unexpected error: ${e.message}")
413
null
414
}
415
}
416
```
417
418
### Performance Considerations
419
420
**JVM Streams:**
421
- **Memory Efficiency**: Stream APIs avoid loading entire JSON into memory
422
- **Sequence Processing**: `decodeToSequence` enables lazy evaluation for large datasets
423
- **I/O Performance**: Direct stream operations reduce intermediate string allocations
424
425
**JavaScript Dynamic Interop:**
426
- **Object Conversion**: Dynamic object conversion has overhead; cache converted objects when possible
427
- **Type Safety**: Runtime type checking is performed during dynamic conversions
428
- **Memory Usage**: Large objects may require additional memory during conversion
429
430
### Platform Detection
431
432
Code using platform-specific APIs should handle multiplatform scenarios appropriately:
433
434
```kotlin
435
// Multiplatform code structure
436
expect fun platformSpecificJsonProcessing(data: String): String
437
438
// JVM implementation
439
actual fun platformSpecificJsonProcessing(data: String): String {
440
return ByteArrayInputStream(data.toByteArray()).use { input ->
441
val objects = Json.decodeToSequence<Map<String, Any>>(input)
442
val processed = objects.map { /* process */ }.toList()
443
Json.encodeToString(processed)
444
}
445
}
446
447
// JS implementation
448
actual fun platformSpecificJsonProcessing(data: String): String {
449
val jsObject = JSON.parse<dynamic>(data)
450
val kotlinObject = Json.decodeFromDynamic<Map<String, Any>>(jsObject)
451
val processed = /* process kotlinObject */
452
return Json.encodeToString(processed)
453
}
454
```