0
# Serialization Support
1
2
Content converter integration for automatic serialization/deserialization of objects to/from WebSocket frames, enabling seamless object-based communication over WebSocket connections.
3
4
## Capabilities
5
6
### Send Serialized Data
7
8
Serialize objects to WebSocket frames and send them through the connection.
9
10
```kotlin { .api }
11
/**
12
* Serialize data to WebSocket frame and send it
13
* May suspend if outgoing queue is full
14
* @param data Object to serialize and send
15
* @param typeInfo Type information for serialization
16
* @throws WebsocketConverterNotFoundException if no converter found
17
*/
18
suspend fun DefaultClientWebSocketSession.sendSerialized(data: Any?, typeInfo: TypeInfo)
19
20
/**
21
* Serialize data to WebSocket frame and send it (reified version)
22
* Type information is automatically inferred
23
* @param data Object to serialize and send
24
* @throws WebsocketConverterNotFoundException if no converter found
25
*/
26
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(data: T)
27
```
28
29
**Usage Examples:**
30
31
```kotlin
32
data class Message(val text: String, val timestamp: Long)
33
data class UserAction(val type: String, val payload: Map<String, Any>)
34
35
client.webSocket("ws://api.example.com") {
36
// Send typed objects (type inferred)
37
sendSerialized(Message("Hello!", System.currentTimeMillis()))
38
sendSerialized(UserAction("login", mapOf("username" to "alice")))
39
40
// Send with explicit type info
41
val genericData: Any = listOf("item1", "item2", "item3")
42
sendSerialized(genericData, typeInfo<List<String>>())
43
44
// Send nullable data
45
val optionalMessage: Message? = null
46
sendSerialized(optionalMessage)
47
}
48
```
49
50
### Receive Deserialized Data
51
52
Receive WebSocket frames and deserialize them to typed objects.
53
54
```kotlin { .api }
55
/**
56
* Receive WebSocket frame and deserialize to specified type
57
* May throw WebsocketDeserializeException if frame cannot be deserialized
58
* @param typeInfo Type information for deserialization
59
* @return Deserialized object of type T
60
* @throws WebsocketConverterNotFoundException if no converter found
61
* @throws WebsocketDeserializeException if deserialization fails
62
* @throws ClosedReceiveChannelException if channel is closed
63
*/
64
suspend fun <T> DefaultClientWebSocketSession.receiveDeserialized(typeInfo: TypeInfo): T
65
66
/**
67
* Receive WebSocket frame and deserialize to specified type (reified version)
68
* Type information is automatically inferred
69
* @return Deserialized object of type T
70
* @throws WebsocketConverterNotFoundException if no converter found
71
* @throws WebsocketDeserializeException if deserialization fails
72
* @throws ClosedReceiveChannelException if channel is closed
73
*/
74
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): T
75
```
76
77
**Usage Examples:**
78
79
```kotlin
80
data class ServerResponse(val status: String, val data: Any?)
81
data class ChatMessage(val user: String, val message: String, val timestamp: Long)
82
83
client.webSocket("ws://chat.example.com") {
84
// Receive typed objects
85
while (true) {
86
try {
87
val response = receiveDeserialized<ServerResponse>()
88
println("Server status: ${response.status}")
89
90
// Handle different response types
91
when (response.status) {
92
"chat_message" -> {
93
val chatMsg = receiveDeserialized<ChatMessage>()
94
println("${chatMsg.user}: ${chatMsg.message}")
95
}
96
"user_list" -> {
97
val users = receiveDeserialized<List<String>>()
98
println("Online users: ${users.joinToString()}")
99
}
100
}
101
102
} catch (e: WebsocketDeserializeException) {
103
println("Failed to deserialize: ${e.message}")
104
// Handle malformed frame
105
val frame = e.frame
106
if (frame is Frame.Text) {
107
println("Raw content: ${frame.readText()}")
108
}
109
}
110
}
111
}
112
```
113
114
### Content Converter Access
115
116
Get the configured content converter for manual serialization operations.
117
118
```kotlin { .api }
119
/**
120
* Get content converter from WebSocket plugin configuration
121
* Returns null if no converter is configured
122
*/
123
val DefaultClientWebSocketSession.converter: WebsocketContentConverter?
124
```
125
126
**Usage Examples:**
127
128
```kotlin
129
client.webSocket("ws://api.example.com") {
130
val converter = converter
131
if (converter != null) {
132
println("Using converter: ${converter::class.simpleName}")
133
134
// Manual serialization
135
val data = MyData("value")
136
val frame = converter.serialize(
137
Charsets.UTF_8,
138
typeInfo<MyData>(),
139
data
140
)
141
outgoing.send(frame)
142
143
} else {
144
println("No content converter configured")
145
// Fall back to manual JSON or string serialization
146
}
147
}
148
```
149
150
### Content Converter Interface
151
152
Interface for implementing custom serialization converters.
153
154
```kotlin { .api }
155
/**
156
* Interface for WebSocket content converters
157
* Handles serialization/deserialization between objects and WebSocket frames
158
*/
159
interface WebsocketContentConverter {
160
/**
161
* Serialize object to WebSocket frame
162
* @param charset Character encoding to use
163
* @param typeInfo Type information for serialization
164
* @param value Object to serialize
165
* @return WebSocket frame containing serialized data
166
*/
167
suspend fun serialize(
168
charset: Charset,
169
typeInfo: TypeInfo,
170
value: Any?
171
): Frame
172
173
/**
174
* Deserialize WebSocket frame to object
175
* @param charset Character encoding to use
176
* @param typeInfo Type information for deserialization
177
* @param content WebSocket frame to deserialize
178
* @return Deserialized object
179
*/
180
suspend fun deserialize(
181
charset: Charset,
182
typeInfo: TypeInfo,
183
content: Frame
184
): Any?
185
}
186
```
187
188
### Exception Types
189
190
Exceptions specific to WebSocket serialization operations.
191
192
```kotlin { .api }
193
/**
194
* Thrown when no content converter is available for serialization
195
* @param message Error description
196
*/
197
class WebsocketConverterNotFoundException(message: String) : Exception
198
199
/**
200
* Thrown when frame deserialization fails
201
* Contains the original frame for inspection
202
* @param message Error description
203
* @param frame Original frame that failed to deserialize
204
*/
205
class WebsocketDeserializeException(
206
message: String,
207
val frame: Frame
208
) : Exception
209
```
210
211
## Serialization Examples
212
213
### JSON Communication
214
215
```kotlin
216
// Configure client with JSON converter
217
val client = HttpClient {
218
install(WebSockets) {
219
contentConverter = GsonWebsocketContentConverter()
220
}
221
}
222
223
data class ApiRequest(val action: String, val params: Map<String, Any>)
224
data class ApiResponse(val success: Boolean, val result: Any?, val error: String?)
225
226
client.webSocket("ws://api.example.com/json") {
227
// Send JSON request
228
sendSerialized(ApiRequest(
229
action = "get_user",
230
params = mapOf("id" to 123)
231
))
232
233
// Receive JSON response
234
val response = receiveDeserialized<ApiResponse>()
235
if (response.success) {
236
println("Result: ${response.result}")
237
} else {
238
println("Error: ${response.error}")
239
}
240
}
241
```
242
243
### Trading Data Stream
244
245
```kotlin
246
data class Trade(
247
val symbol: String,
248
val price: Double,
249
val quantity: Double,
250
val timestamp: Long
251
)
252
253
data class OrderBook(
254
val symbol: String,
255
val bids: List<Pair<Double, Double>>,
256
val asks: List<Pair<Double, Double>>
257
)
258
259
client.webSocket("ws://trading.example.com") {
260
// Subscribe to data streams
261
sendSerialized(mapOf(
262
"action" to "subscribe",
263
"channels" to listOf("trades", "orderbook"),
264
"symbols" to listOf("BTCUSD", "ETHUSD")
265
))
266
267
while (true) {
268
try {
269
// Receive different message types
270
val messageType = receiveDeserialized<Map<String, Any>>()
271
272
when (messageType["type"]) {
273
"trade" -> {
274
val trade = receiveDeserialized<Trade>()
275
handleTrade(trade)
276
}
277
278
"orderbook" -> {
279
val orderBook = receiveDeserialized<OrderBook>()
280
handleOrderBook(orderBook)
281
}
282
283
"error" -> {
284
val error = receiveDeserialized<Map<String, String>>()
285
println("Server error: ${error["message"]}")
286
}
287
}
288
289
} catch (e: WebsocketDeserializeException) {
290
println("Failed to parse message: ${e.message}")
291
292
// Log raw frame content for debugging
293
when (val frame = e.frame) {
294
is Frame.Text -> println("Raw text: ${frame.readText()}")
295
is Frame.Binary -> println("Raw binary: ${frame.data.size} bytes")
296
}
297
}
298
}
299
}
300
```
301
302
### Custom Protocol with Kotlinx Serialization
303
304
```kotlin
305
@Serializable
306
data class GameState(
307
val players: List<Player>,
308
val board: Array<Array<Int>>,
309
val currentPlayer: String,
310
val gameOver: Boolean
311
)
312
313
@Serializable
314
data class GameAction(
315
val player: String,
316
val action: String,
317
val coordinates: Pair<Int, Int>?
318
)
319
320
// Configure client with Kotlinx Serialization
321
val client = HttpClient {
322
install(WebSockets) {
323
contentConverter = KotlinxSerializationWebsocketContentConverter(Json)
324
}
325
}
326
327
client.webSocket("ws://game.example.com") {
328
// Send game action
329
sendSerialized(GameAction(
330
player = "alice",
331
action = "move",
332
coordinates = Pair(2, 3)
333
))
334
335
// Receive updated game state
336
val newState = receiveDeserialized<GameState>()
337
updateGameUI(newState)
338
}
339
```
340
341
### Mixed Text and Binary Serialization
342
343
```kotlin
344
client.webSocket("ws://multimedia.example.com") {
345
// Text-based metadata
346
sendSerialized(mapOf(
347
"type" to "image_upload",
348
"filename" to "photo.jpg",
349
"size" to 1024000
350
))
351
352
// Binary image data (using custom converter that produces binary frames)
353
val imageBytes = File("photo.jpg").readBytes()
354
val converter = converter as? CustomBinaryConverter
355
356
if (converter != null) {
357
val binaryFrame = converter.serialize(
358
Charsets.UTF_8,
359
typeInfo<ByteArray>(),
360
imageBytes
361
) as Frame.Binary
362
363
outgoing.send(binaryFrame)
364
}
365
366
// Receive processing result
367
val result = receiveDeserialized<Map<String, Any>>()
368
println("Upload result: ${result["status"]}")
369
}
370
```
371
372
### Error Handling and Fallbacks
373
374
```kotlin
375
client.webSocket("ws://api.example.com") {
376
try {
377
// Attempt to send typed object
378
sendSerialized(MyComplexType(data))
379
380
} catch (e: WebsocketConverterNotFoundException) {
381
// Fallback to manual JSON serialization
382
val json = Json.encodeToString(data)
383
send(json)
384
}
385
386
// Receive with error handling
387
while (true) {
388
try {
389
val response = receiveDeserialized<ApiResponse>()
390
handleResponse(response)
391
392
} catch (e: WebsocketDeserializeException) {
393
// Try to handle as plain text
394
val frame = e.frame
395
if (frame is Frame.Text) {
396
val plainText = frame.readText()
397
println("Received plain text: $plainText")
398
399
// Attempt manual parsing
400
try {
401
val json = Json.parseToJsonElement(plainText)
402
handleJsonElement(json)
403
} catch (e: Exception) {
404
println("Could not parse as JSON: $plainText")
405
}
406
}
407
408
} catch (e: ClosedReceiveChannelException) {
409
println("Connection closed")
410
break
411
}
412
}
413
}
414
```
415
416
### Batch Operations
417
418
```kotlin
419
client.webSocket("ws://batch.example.com") {
420
// Send multiple objects in sequence
421
val messages = listOf(
422
ChatMessage("alice", "Hello everyone!"),
423
ChatMessage("bob", "Hi Alice!"),
424
ChatMessage("charlie", "Good morning!")
425
)
426
427
for (message in messages) {
428
sendSerialized(message)
429
}
430
431
// Receive batch response
432
val batchResult = receiveDeserialized<List<MessageStatus>>()
433
batchResult.forEach { status ->
434
println("Message ${status.id}: ${status.delivered}")
435
}
436
}
437
```
438
439
### Type-Safe Protocol Definition
440
441
```kotlin
442
sealed class ServerMessage {
443
@Serializable
444
data class UserJoined(val username: String) : ServerMessage()
445
446
@Serializable
447
data class UserLeft(val username: String) : ServerMessage()
448
449
@Serializable
450
data class ChatMessage(val user: String, val message: String) : ServerMessage()
451
452
@Serializable
453
data class SystemNotification(val level: String, val text: String) : ServerMessage()
454
}
455
456
client.webSocket("ws://chat.example.com") {
457
// Type-safe message handling
458
while (true) {
459
val message = receiveDeserialized<ServerMessage>()
460
461
when (message) {
462
is ServerMessage.UserJoined ->
463
println("${message.username} joined the chat")
464
465
is ServerMessage.UserLeft ->
466
println("${message.username} left the chat")
467
468
is ServerMessage.ChatMessage ->
469
println("${message.user}: ${message.message}")
470
471
is ServerMessage.SystemNotification ->
472
println("[${message.level}] ${message.text}")
473
}
474
}
475
}
476
```