0
# Content Serialization
1
2
Ktor Client WebSockets provides automatic serialization and deserialization of objects through WebSocket frames using Ktor's content conversion system.
3
4
## Serialization Functions
5
6
### sendSerialized() - Send Serialized Objects
7
8
Send objects as WebSocket frames with automatic serialization:
9
10
```kotlin { .api }
11
suspend fun DefaultClientWebSocketSession.sendSerialized(
12
data: Any?,
13
typeInfo: TypeInfo
14
)
15
16
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(
17
data: T
18
)
19
```
20
21
**Parameters:**
22
- **data**: Object to serialize and send
23
- **typeInfo**: Type information for serialization (manual variant only)
24
25
**Usage:**
26
```kotlin
27
import io.ktor.client.plugins.websocket.*
28
import kotlinx.serialization.*
29
30
@Serializable
31
data class Message(val text: String, val timestamp: Long)
32
33
client.webSocket("ws://example.com/api") {
34
val message = Message("Hello World", System.currentTimeMillis())
35
36
// Send with reified type (recommended)
37
sendSerialized(message)
38
39
// Send with explicit TypeInfo
40
sendSerialized(message, typeInfo<Message>())
41
}
42
```
43
44
### receiveDeserialized() - Receive and Deserialize Objects
45
46
Receive WebSocket frames and automatically deserialize to objects:
47
48
```kotlin { .api }
49
suspend fun <T> DefaultClientWebSocketSession.receiveDeserialized(
50
typeInfo: TypeInfo
51
): T
52
53
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): T
54
```
55
56
**Parameters:**
57
- **typeInfo**: Type information for deserialization (manual variant only)
58
59
**Returns:**
60
- Deserialized object of type T
61
62
**Usage:**
63
```kotlin
64
client.webSocket("ws://example.com/api") {
65
// Receive with reified type (recommended)
66
val message = receiveDeserialized<Message>()
67
println("Received: ${message.text} at ${message.timestamp}")
68
69
// Receive with explicit TypeInfo
70
val message2 = receiveDeserialized<Message>(typeInfo<Message>())
71
println("Received: ${message2.text}")
72
}
73
```
74
75
## Content Converter Configuration
76
77
Content serialization requires a WebSocket content converter to be configured in the WebSockets plugin:
78
79
```kotlin { .api }
80
var WebSockets.Config.contentConverter: WebsocketContentConverter?
81
```
82
83
### JSON Serialization Setup
84
85
Using kotlinx.serialization with JSON:
86
87
```kotlin
88
import io.ktor.client.*
89
import io.ktor.client.plugins.websocket.*
90
import io.ktor.serialization.kotlinx.*
91
import kotlinx.serialization.json.*
92
93
val client = HttpClient {
94
install(WebSockets) {
95
contentConverter = KotlinxWebsocketSerializationConverter(Json {
96
prettyPrint = true
97
isLenient = true
98
ignoreUnknownKeys = true
99
})
100
}
101
}
102
```
103
104
### Custom Content Converter
105
106
Implement custom serialization logic:
107
108
```kotlin
109
import io.ktor.websocket.*
110
import io.ktor.util.*
111
import io.ktor.util.reflect.*
112
113
class MyWebSocketConverter : WebsocketContentConverter {
114
override suspend fun serialize(
115
charset: Charset,
116
typeInfo: TypeInfo,
117
value: Any?
118
): Frame {
119
// Custom serialization logic
120
val serialized = mySerialize(value)
121
return Frame.Text(serialized)
122
}
123
124
override suspend fun deserialize(
125
charset: Charset,
126
typeInfo: TypeInfo,
127
content: Frame
128
): Any? {
129
// Custom deserialization logic
130
return when (content) {
131
is Frame.Text -> myDeserialize(content.readText(), typeInfo)
132
is Frame.Binary -> myDeserializeBinary(content.data, typeInfo)
133
else -> throw WebSocketException("Unsupported frame type for deserialization")
134
}
135
}
136
137
override fun isApplicable(frame: Frame): Boolean {
138
return frame is Frame.Text || frame is Frame.Binary
139
}
140
}
141
142
// Install custom converter
143
val client = HttpClient {
144
install(WebSockets) {
145
contentConverter = MyWebSocketConverter()
146
}
147
}
148
```
149
150
## Serialization Examples
151
152
### Basic Object Serialization
153
154
```kotlin
155
import kotlinx.serialization.*
156
157
@Serializable
158
data class ChatMessage(
159
val user: String,
160
val message: String,
161
val timestamp: Long = System.currentTimeMillis()
162
)
163
164
client.webSocket("ws://chat.example.com/room/general") {
165
// Send chat message
166
val chatMsg = ChatMessage("alice", "Hello everyone!")
167
sendSerialized(chatMsg)
168
169
// Receive chat messages
170
for (frame in incoming) {
171
when (frame) {
172
is Frame.Text -> {
173
try {
174
val received = receiveDeserialized<ChatMessage>()
175
println("${received.user}: ${received.message}")
176
} catch (e: Exception) {
177
println("Failed to deserialize: ${frame.readText()}")
178
}
179
}
180
is Frame.Close -> break
181
else -> { /* Handle other frame types */ }
182
}
183
}
184
}
185
```
186
187
### Complex Data Structures
188
189
```kotlin
190
@Serializable
191
data class GameState(
192
val players: List<Player>,
193
val currentTurn: String,
194
val board: Map<String, String>,
195
val settings: GameSettings
196
)
197
198
@Serializable
199
data class Player(val id: String, val name: String, val score: Int)
200
201
@Serializable
202
data class GameSettings(val maxPlayers: Int, val timeLimit: Long)
203
204
client.webSocket("ws://game.example.com/session/123") {
205
// Send complex game state
206
val gameState = GameState(
207
players = listOf(
208
Player("p1", "Alice", 100),
209
Player("p2", "Bob", 85)
210
),
211
currentTurn = "p1",
212
board = mapOf("a1" to "X", "b2" to "O"),
213
settings = GameSettings(maxPlayers = 4, timeLimit = 300_000)
214
)
215
sendSerialized(gameState)
216
217
// Receive updated game state
218
val updatedState = receiveDeserialized<GameState>()
219
println("Game updated: ${updatedState.players.size} players")
220
}
221
```
222
223
### Mixed Content Communication
224
225
Combine serialized objects with raw frames:
226
227
```kotlin
228
client.webSocket("ws://api.example.com/mixed") {
229
// Send serialized object
230
sendSerialized(ChatMessage("system", "User connected"))
231
232
// Send raw text frame
233
send("Raw status message")
234
235
// Receive mixed content
236
for (frame in incoming) {
237
when (frame) {
238
is Frame.Text -> {
239
val text = frame.readText()
240
241
// Try to deserialize as known type
242
try {
243
val message = receiveDeserialized<ChatMessage>()
244
println("Structured: ${message.user} - ${message.message}")
245
} catch (e: Exception) {
246
// Handle as raw text
247
println("Raw: $text")
248
}
249
}
250
is Frame.Close -> break
251
else -> { /* Handle other frames */ }
252
}
253
}
254
}
255
```
256
257
## Error Handling
258
259
### Serialization Errors
260
261
Handle serialization failures gracefully:
262
263
```kotlin
264
client.webSocket("ws://example.com/api") {
265
try {
266
val message = Message("test", System.currentTimeMillis())
267
sendSerialized(message)
268
} catch (e: SerializationException) {
269
println("Failed to serialize message: ${e.message}")
270
// Fallback to raw frame
271
send("Serialization failed - sending raw text")
272
} catch (e: Exception) {
273
println("Unexpected error during serialization: ${e.message}")
274
}
275
}
276
```
277
278
### Deserialization Errors
279
280
Handle deserialization failures:
281
282
```kotlin
283
client.webSocket("ws://example.com/api") {
284
for (frame in incoming) {
285
when (frame) {
286
is Frame.Text -> {
287
try {
288
val message = receiveDeserialized<Message>()
289
processMessage(message)
290
} catch (e: SerializationException) {
291
println("Failed to deserialize frame: ${e.message}")
292
// Handle as raw text
293
val rawText = frame.readText()
294
processRawMessage(rawText)
295
} catch (e: Exception) {
296
println("Unexpected deserialization error: ${e.message}")
297
}
298
}
299
is Frame.Close -> break
300
else -> { /* Handle other frame types */ }
301
}
302
}
303
}
304
```
305
306
### Missing Content Converter
307
308
Handle cases where no content converter is configured:
309
310
```kotlin
311
client.webSocket("ws://example.com/api") {
312
if (converter != null) {
313
// Use serialization
314
sendSerialized(MyData("test"))
315
val response = receiveDeserialized<MyResponse>()
316
} else {
317
// Fall back to raw frames
318
send("Raw message without serialization")
319
val frame = incoming.receive()
320
if (frame is Frame.Text) {
321
val response = frame.readText()
322
println("Raw response: $response")
323
}
324
}
325
}
326
```
327
328
## Binary Serialization
329
330
Handle binary data serialization:
331
332
```kotlin
333
@Serializable
334
data class BinaryMessage(
335
val header: String,
336
val payload: ByteArray
337
) {
338
override fun equals(other: Any?): Boolean {
339
if (this === other) return true
340
if (other !is BinaryMessage) return false
341
if (header != other.header) return false
342
if (!payload.contentEquals(other.payload)) return false
343
return true
344
}
345
346
override fun hashCode(): Int {
347
var result = header.hashCode()
348
result = 31 * result + payload.contentHashCode()
349
return result
350
}
351
}
352
353
// Configure for binary serialization
354
val client = HttpClient {
355
install(WebSockets) {
356
contentConverter = KotlinxWebsocketSerializationConverter(
357
ProtoBuf // or other binary format
358
)
359
}
360
}
361
362
client.webSocket("ws://example.com/binary") {
363
// Send binary message
364
val binaryMsg = BinaryMessage("header", byteArrayOf(1, 2, 3, 4))
365
sendSerialized(binaryMsg)
366
367
// Receive binary message
368
val received = receiveDeserialized<BinaryMessage>()
369
println("Header: ${received.header}, Payload size: ${received.payload.size}")
370
}
371
```
372
373
## Performance Considerations
374
375
### Chunked Serialization
376
377
For large objects, consider chunking:
378
379
```kotlin
380
@Serializable
381
data class LargeDataSet(val items: List<DataItem>)
382
383
client.webSocket("ws://example.com/large-data") {
384
val largeData = generateLargeDataSet()
385
386
// Check frame size limits
387
if (maxFrameSize < estimateSerializedSize(largeData)) {
388
// Chunk the data
389
largeData.items.chunked(100).forEach { chunk ->
390
sendSerialized(DataChunk(chunk))
391
}
392
sendSerialized(EndOfData())
393
} else {
394
// Send as single frame
395
sendSerialized(largeData)
396
}
397
}
398
```
399
400
### Streaming Serialization
401
402
For continuous data streams:
403
404
```kotlin
405
client.webSocket("ws://example.com/stream") {
406
// Stream data as individual serialized frames
407
generateDataStream().collect { dataPoint ->
408
sendSerialized(dataPoint)
409
410
// Optional: Add throttling
411
delay(10) // 100 FPS max
412
}
413
}
414
```
415
416
## Content Type Handling
417
418
Different serialization formats can be configured:
419
420
```kotlin
421
// JSON serialization
422
install(WebSockets) {
423
contentConverter = KotlinxWebsocketSerializationConverter(Json)
424
}
425
426
// Protocol Buffers serialization
427
install(WebSockets) {
428
contentConverter = KotlinxWebsocketSerializationConverter(ProtoBuf)
429
}
430
431
// CBOR serialization
432
install(WebSockets) {
433
contentConverter = KotlinxWebsocketSerializationConverter(Cbor)
434
}
435
436
// XML serialization
437
install(WebSockets) {
438
contentConverter = KotlinxWebsocketSerializationConverter(XML)
439
}
440
```