or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdconfiguration.mdcore-operations.mdcustom-serializers.mddsl-builders.mdindex.mdjson-elements.mdnaming-strategies.mdplatform-extensions.md

platform-extensions.mddocs/

0

# Platform Extensions

1

2

Platform-specific extensions providing additional functionality for JVM streams and JavaScript dynamic objects.

3

4

## Capabilities

5

6

### JVM Stream Extensions

7

8

JVM-specific extensions for working with InputStream and OutputStream for efficient streaming operations.

9

10

```kotlin { .api }

11

/**

12

* Serializes the value with serializer into a stream using JSON format and UTF-8 encoding

13

* @param serializer Strategy for serializing type T

14

* @param value Value to serialize

15

* @param stream OutputStream to write JSON to

16

* @throws SerializationException if value cannot be serialized to JSON

17

* @throws IOException if I/O error occurs during writing

18

*/

19

fun <T> Json.encodeToStream(

20

serializer: SerializationStrategy<T>,

21

value: T,

22

stream: OutputStream

23

)

24

25

/**

26

* Serializes value to stream using serializer retrieved from reified type parameter

27

* @param value Value to serialize

28

* @param stream OutputStream to write JSON to

29

* @throws SerializationException if value cannot be serialized to JSON

30

* @throws IOException if I/O error occurs during writing

31

*/

32

inline fun <reified T> Json.encodeToStream(value: T, stream: OutputStream)

33

34

/**

35

* Deserializes JSON from stream using UTF-8 encoding to value of type T

36

* Expects exactly one object in the stream

37

* @param deserializer Strategy for deserializing type T

38

* @param stream InputStream to read JSON from

39

* @return Deserialized value of type T

40

* @throws SerializationException if JSON input cannot be deserialized

41

* @throws IllegalArgumentException if decoded input is invalid for type T

42

* @throws IOException if I/O error occurs during reading

43

*/

44

fun <T> Json.decodeFromStream(

45

deserializer: DeserializationStrategy<T>,

46

stream: InputStream

47

): T

48

49

/**

50

* Deserializes from stream using deserializer retrieved from reified type parameter

51

* @param stream InputStream to read JSON from

52

* @return Deserialized value of type T

53

* @throws SerializationException if JSON input cannot be deserialized

54

* @throws IllegalArgumentException if decoded input is invalid for type T

55

* @throws IOException if I/O error occurs during reading

56

*/

57

inline fun <reified T> Json.decodeFromStream(stream: InputStream): T

58

59

/**

60

* Transforms stream into lazily deserialized sequence of elements of type T

61

* Stream can contain multiple elements separated as format declares

62

* @param stream InputStream to read JSON sequence from

63

* @param deserializer Strategy for deserializing type T

64

* @param format How elements are separated in the stream

65

* @return Sequence of deserialized elements (evaluated lazily)

66

* @throws SerializationException if JSON input cannot be deserialized

67

* @throws IllegalArgumentException if decoded input is invalid for type T

68

* @throws IOException if I/O error occurs during reading

69

*/

70

fun <T> Json.decodeToSequence(

71

stream: InputStream,

72

deserializer: DeserializationStrategy<T>,

73

format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT

74

): Sequence<T>

75

76

/**

77

* Reified version of decodeToSequence

78

* @param stream InputStream to read JSON sequence from

79

* @param format How elements are separated in the stream

80

* @return Sequence of deserialized elements (evaluated lazily)

81

*/

82

inline fun <reified T> Json.decodeToSequence(

83

stream: InputStream,

84

format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT

85

): Sequence<T>

86

```

87

88

**Usage Examples:**

89

90

```kotlin

91

@Serializable

92

data class LogEntry(

93

val timestamp: Long,

94

val level: String,

95

val message: String,

96

val metadata: Map<String, String> = emptyMap()

97

)

98

99

// Encoding to stream

100

val logEntry = LogEntry(

101

timestamp = System.currentTimeMillis(),

102

level = "INFO",

103

message = "Application started",

104

metadata = mapOf("version" to "1.0.0", "env" to "production")

105

)

106

107

// Write to file

108

val outputFile = File("application.log")

109

outputFile.outputStream().use { stream ->

110

Json.encodeToStream(logEntry, stream)

111

}

112

113

// Write to ByteArrayOutputStream

114

val byteArrayStream = ByteArrayOutputStream()

115

Json.encodeToStream(LogEntry.serializer(), logEntry, byteArrayStream)

116

val jsonBytes = byteArrayStream.toByteArray()

117

118

// Decoding from stream

119

val inputFile = File("application.log")

120

val decodedEntry = inputFile.inputStream().use { stream ->

121

Json.decodeFromStream<LogEntry>(stream)

122

}

123

124

// Decoding with explicit deserializer

125

val anotherEntry = inputFile.inputStream().use { stream ->

126

Json.decodeFromStream(LogEntry.serializer(), stream)

127

}

128

129

// Stream processing with sequence

130

@Serializable

131

data class SensorReading(

132

val sensorId: String,

133

val value: Double,

134

val timestamp: Long

135

)

136

137

// Multiple readings in whitespace-separated format

138

val readingsData = """

139

{"sensorId":"temp01","value":23.5,"timestamp":1634567890123}

140

{"sensorId":"temp01","value":24.1,"timestamp":1634567890223}

141

{"sensorId":"temp01","value":23.8,"timestamp":1634567890323}

142

{"sensorId":"temp02","value":22.3,"timestamp":1634567890423}

143

"""

144

145

val readingsStream = ByteArrayInputStream(readingsData.toByteArray())

146

val readings = Json.decodeToSequence<SensorReading>(

147

readingsStream,

148

DecodeSequenceMode.WHITESPACE_SEPARATED

149

)

150

151

// Process readings lazily

152

readings.forEach { reading ->

153

println("Sensor ${reading.sensorId}: ${reading.value}°C at ${reading.timestamp}")

154

}

155

156

// Array-wrapped sequence

157

val arrayWrappedData = """

158

[

159

{"sensorId":"temp01","value":25.0,"timestamp":1634567890523},

160

{"sensorId":"temp02","value":23.5,"timestamp":1634567890623},

161

{"sensorId":"temp03","value":24.2,"timestamp":1634567890723}

162

]

163

"""

164

165

val arrayStream = ByteArrayInputStream(arrayWrappedData.toByteArray())

166

val arrayReadings = Json.decodeToSequence<SensorReading>(

167

arrayStream,

168

DecodeSequenceMode.ARRAY_WRAPPED

169

)

170

171

// Filter and transform during processing

172

val highTempReadings = arrayReadings

173

.filter { it.value > 24.0 }

174

.map { "${it.sensorId}: ${it.value}°C" }

175

.toList()

176

177

// Large file processing

178

val largeLogFile = File("large-log.jsonl") // JSON Lines format

179

val errorEntries = largeLogFile.inputStream().use { stream ->

180

Json.decodeToSequence<LogEntry>(stream, DecodeSequenceMode.WHITESPACE_SEPARATED)

181

.filter { it.level == "ERROR" }

182

.take(100) // Only process first 100 errors

183

.toList()

184

}

185

```

186

187

### JavaScript Dynamic Extensions

188

189

JavaScript-specific extensions for working with native JavaScript objects using the `dynamic` type.

190

191

```kotlin { .api }

192

/**

193

* Converts native JavaScript objects into Kotlin ones, verifying their types

194

* Equivalent to Json.decodeFromString(JSON.stringify(nativeObj))

195

* @param deserializer Strategy for deserializing type T

196

* @param dynamic Native JavaScript object to convert

197

* @return Kotlin object of type T

198

* @throws SerializationException if dynamic object cannot be converted to T

199

*/

200

fun <T> Json.decodeFromDynamic(deserializer: DeserializationStrategy<T>, dynamic: dynamic): T

201

202

/**

203

* Reified version of decodeFromDynamic

204

* @param dynamic Native JavaScript object to convert

205

* @return Kotlin object of type T

206

*/

207

inline fun <reified T> Json.decodeFromDynamic(dynamic: dynamic): T

208

209

/**

210

* Converts Kotlin data structures to plain JavaScript objects

211

* @param serializer Strategy for serializing type T

212

* @param value Kotlin object to convert

213

* @return Native JavaScript object

214

* @throws SerializationException if value cannot be converted to dynamic

215

*/

216

fun <T> Json.encodeToDynamic(serializer: SerializationStrategy<T>, value: T): dynamic

217

218

/**

219

* Reified version of encodeToDynamic

220

* @param value Kotlin object to convert

221

* @return Native JavaScript object

222

*/

223

inline fun <reified T> Json.encodeToDynamic(value: T): dynamic

224

```

225

226

**Usage Examples:**

227

228

```kotlin

229

@Serializable

230

data class UserProfile(

231

val id: String,

232

val name: String,

233

val email: String,

234

val preferences: UserPreferences

235

)

236

237

@Serializable

238

data class UserPreferences(

239

val theme: String,

240

val notifications: Boolean,

241

val language: String

242

)

243

244

// Working with JavaScript objects from APIs

245

val jsApiResponse: dynamic = window.fetch("/api/user/profile")

246

.then { response -> response.json() }

247

.await()

248

249

// Convert JavaScript object to Kotlin

250

val userProfile = Json.decodeFromDynamic<UserProfile>(jsApiResponse)

251

252

// With explicit deserializer

253

val explicitProfile = Json.decodeFromDynamic(UserProfile.serializer(), jsApiResponse)

254

255

// Convert Kotlin object to JavaScript

256

val preferences = UserPreferences(

257

theme = "dark",

258

notifications = true,

259

language = "en"

260

)

261

262

val jsPreferences = Json.encodeToDynamic(preferences)

263

// jsPreferences is now a native JavaScript object

264

265

// Can be used directly with JavaScript APIs

266

window.localStorage.setItem("preferences", JSON.stringify(jsPreferences))

267

268

// Working with mixed JavaScript/Kotlin code

269

@Serializable

270

data class ApiRequest(

271

val method: String,

272

val endpoint: String,

273

val data: JsonElement?

274

)

275

276

// Create request in Kotlin

277

val apiRequest = ApiRequest(

278

method = "POST",

279

endpoint = "/api/users",

280

data = buildJsonObject {

281

put("name", "Alice")

282

put("email", "alice@example.com")

283

}

284

)

285

286

// Convert to JavaScript object for fetch API

287

val jsRequest = Json.encodeToDynamic(apiRequest)

288

289

// Use with native fetch

290

val fetchOptions = js("{}")

291

fetchOptions.method = jsRequest.method

292

fetchOptions.body = JSON.stringify(jsRequest.data)

293

fetchOptions.headers = js("{}")

294

fetchOptions.headers["Content-Type"] = "application/json"

295

296

val response = window.fetch(jsRequest.endpoint, fetchOptions).await()

297

298

// Process JavaScript response

299

val jsResponseData: dynamic = response.json().await()

300

301

@Serializable

302

data class ApiResponse(

303

val success: Boolean,

304

val data: JsonElement?,

305

val message: String?

306

)

307

308

val apiResponse = Json.decodeFromDynamic<ApiResponse>(jsResponseData)

309

310

// Interop with existing JavaScript libraries

311

// Assuming a JavaScript chart library expects configuration object

312

@Serializable

313

data class ChartConfig(

314

val type: String,

315

val data: ChartData,

316

val options: ChartOptions

317

)

318

319

@Serializable

320

data class ChartData(

321

val labels: List<String>,

322

val datasets: List<Dataset>

323

)

324

325

@Serializable

326

data class Dataset(

327

val label: String,

328

val data: List<Double>,

329

val backgroundColor: String

330

)

331

332

@Serializable

333

data class ChartOptions(

334

val responsive: Boolean,

335

val maintainAspectRatio: Boolean

336

)

337

338

val chartConfig = ChartConfig(

339

type = "bar",

340

data = ChartData(

341

labels = listOf("Jan", "Feb", "Mar", "Apr"),

342

datasets = listOf(

343

Dataset(

344

label = "Sales",

345

data = listOf(100.0, 150.0, 200.0, 175.0),

346

backgroundColor = "#36A2EB"

347

)

348

)

349

),

350

options = ChartOptions(

351

responsive = true,

352

maintainAspectRatio = false

353

)

354

)

355

356

// Convert to JavaScript object for chart library

357

val jsChartConfig = Json.encodeToDynamic(chartConfig)

358

359

// Use with Chart.js (example)

360

val canvas = document.getElementById("myChart") as HTMLCanvasElement

361

val chart = Chart(canvas, jsChartConfig)

362

363

// Working with complex nested JavaScript objects

364

val complexJsObject: dynamic = js("""

365

{

366

user: {

367

profile: {

368

id: "user123",

369

name: "Alice",

370

email: "alice@example.com",

371

preferences: {

372

theme: "dark",

373

notifications: true,

374

language: "en"

375

}

376

},

377

permissions: ["read", "write"],

378

metadata: {

379

lastLogin: 1634567890123,

380

loginCount: 45

381

}

382

}

383

}

384

""")

385

386

// Extract specific parts

387

val userProfileJs = complexJsObject.user.profile

388

val kotlinProfile = Json.decodeFromDynamic<UserProfile>(userProfileJs)

389

390

// Handle arrays from JavaScript

391

val permissionsJs = complexJsObject.user.permissions

392

val permissions = Json.decodeFromDynamic<List<String>>(permissionsJs)

393

394

// Convert metadata map

395

val metadataJs = complexJsObject.user.metadata

396

val metadata = Json.decodeFromDynamic<Map<String, Long>>(metadataJs)

397

```

398

399

### Platform-Specific Considerations

400

401

Important considerations when using platform extensions.

402

403

**Usage Examples:**

404

405

```kotlin

406

// JVM: Resource management

407

class LogProcessor {

408

fun processLogFile(filePath: String): List<LogEntry> {

409

return File(filePath).inputStream().use { stream ->

410

// Stream is automatically closed even if exception occurs

411

Json.decodeToSequence<LogEntry>(stream, DecodeSequenceMode.WHITESPACE_SEPARATED)

412

.filter { it.level in setOf("ERROR", "WARN") }

413

.toList()

414

}

415

}

416

417

fun exportLogs(entries: List<LogEntry>, outputPath: String) {

418

File(outputPath).outputStream().use { stream ->

419

// Write each entry as separate JSON object

420

entries.forEach { entry ->

421

Json.encodeToStream(entry, stream)

422

stream.write("\n".toByteArray()) // Add newline

423

}

424

}

425

}

426

}

427

428

// JavaScript: Type safety with dynamic objects

429

fun processApiResponse(jsResponse: dynamic): UserProfile? {

430

return try {

431

// Validate structure before converting

432

if (jsResponse.id == undefined || jsResponse.name == undefined) {

433

return null

434

}

435

436

Json.decodeFromDynamic<UserProfile>(jsResponse)

437

} catch (e: SerializationException) {

438

console.error("Failed to parse user profile", e)

439

null

440

}

441

}

442

443

// JavaScript: Working with promises and async operations

444

suspend fun fetchUserData(userId: String): UserProfile? {

445

val response = window.fetch("/api/users/$userId").await()

446

447

if (!response.ok) {

448

return null

449

}

450

451

val jsData: dynamic = response.json().await()

452

return Json.decodeFromDynamic<UserProfile>(jsData)

453

}

454

455

// JavaScript: Integration with localStorage

456

fun saveUserPreferences(prefs: UserPreferences) {

457

val jsPrefs = Json.encodeToDynamic(prefs)

458

window.localStorage.setItem("userPreferences", JSON.stringify(jsPrefs))

459

}

460

461

fun loadUserPreferences(): UserPreferences? {

462

val stored = window.localStorage.getItem("userPreferences") ?: return null

463

val jsPrefs = JSON.parse(stored)

464

465

return try {

466

Json.decodeFromDynamic<UserPreferences>(jsPrefs)

467

} catch (e: Exception) {

468

null

469

}

470

}

471

472

// Multiplatform considerations

473

expect class PlatformSerializer() {

474

fun serializeForPlatform(data: Any): String

475

fun deserializeFromPlatform(json: String): Any?

476

}

477

478

// JVM implementation

479

actual class PlatformSerializer {

480

actual fun serializeForPlatform(data: Any): String {

481

// Use stream-based serialization for large objects

482

val stream = ByteArrayOutputStream()

483

when (data) {

484

is Serializable -> {

485

Json.encodeToStream(data, stream)

486

return stream.toString(Charsets.UTF_8)

487

}

488

else -> return Json.encodeToString(data.toString())

489

}

490

}

491

492

actual fun deserializeFromPlatform(json: String): Any? {

493

return try {

494

val stream = ByteArrayInputStream(json.toByteArray())

495

Json.decodeFromStream<JsonElement>(stream)

496

} catch (e: Exception) {

497

null

498

}

499

}

500

}

501

502

// JavaScript implementation

503

actual class PlatformSerializer {

504

actual fun serializeForPlatform(data: Any): String {

505

// Use dynamic serialization for JavaScript interop

506

val dynamic = Json.encodeToDynamic(data)

507

return JSON.stringify(dynamic)

508

}

509

510

actual fun deserializeFromPlatform(json: String): Any? {

511

return try {

512

val jsObject = JSON.parse(json)

513

Json.decodeFromDynamic<JsonElement>(jsObject)

514

} catch (e: Exception) {

515

null

516

}

517

}

518

}

519

```