or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arithmetic.mdformatting.mdindex.mdinstant.mdlocal-types.mdplatform.mdranges.mdserialization.mdtimezones.md

serialization.mddocs/

0

# Serialization Support

1

2

Comprehensive kotlinx.serialization support with multiple serializer types for different use cases. The library provides default serializers, explicit ISO 8601 serializers, component-based serializers, and specialized serializers for different data formats.

3

4

## Capabilities

5

6

### Default Serializers

7

8

Standard serializers that delegate to the `toString()` and `parse()` methods of each type, providing automatic ISO 8601 compliance.

9

10

#### Core Type Serializers

11

12

```kotlin { .api }

13

/**

14

* Default serializer for kotlin.time.Instant

15

* Uses ISO 8601 format via toString/parse delegation

16

*/

17

object InstantSerializer : KSerializer<Instant>

18

19

/**

20

* Default serializer for LocalDate

21

* Uses ISO 8601 extended format (YYYY-MM-DD)

22

*/

23

object LocalDateSerializer : KSerializer<LocalDate>

24

25

/**

26

* Default serializer for LocalTime

27

* Uses ISO 8601 extended format (HH:MM:SS[.fff])

28

*/

29

object LocalTimeSerializer : KSerializer<LocalTime>

30

31

/**

32

* Default serializer for LocalDateTime

33

* Uses ISO 8601 extended format (YYYY-MM-DDTHH:MM:SS[.fff])

34

*/

35

object LocalDateTimeSerializer : KSerializer<LocalDateTime>

36

37

/**

38

* Default serializer for YearMonth

39

* Uses ISO 8601 format (YYYY-MM)

40

*/

41

object YearMonthSerializer : KSerializer<YearMonth>

42

43

/**

44

* Default serializer for UtcOffset

45

* Uses ISO 8601 format (+HH:MM, Z, etc.)

46

*/

47

object UtcOffsetSerializer : KSerializer<UtcOffset>

48

```

49

50

#### Time Zone Serializers

51

52

```kotlin { .api }

53

/**

54

* Default serializer for TimeZone

55

* Serializes the time zone ID as a string

56

*/

57

object TimeZoneSerializer : KSerializer<TimeZone>

58

59

/**

60

* Default serializer for FixedOffsetTimeZone

61

* Serializes as the offset string representation

62

*/

63

object FixedOffsetTimeZoneSerializer : KSerializer<FixedOffsetTimeZone>

64

```

65

66

#### Period and Unit Serializers

67

68

```kotlin { .api }

69

/**

70

* Default serializer for DateTimePeriod

71

* Uses ISO 8601 duration format via toString/parse

72

*/

73

object DateTimePeriodSerializer : KSerializer<DateTimePeriod>

74

75

/**

76

* Default serializer for DatePeriod

77

* Uses ISO 8601 date duration format via toString/parse

78

*/

79

object DatePeriodSerializer : KSerializer<DatePeriod>

80

81

/**

82

* Default serializer for DateTimeUnit

83

* Serializes unit representation

84

*/

85

object DateTimeUnitSerializer : KSerializer<DateTimeUnit>

86

```

87

88

#### Enumeration Serializers

89

90

```kotlin { .api }

91

/**

92

* Default serializer for Month enum

93

* Serializes as enum name

94

*/

95

object MonthSerializer : KSerializer<Month>

96

97

/**

98

* Default serializer for DayOfWeek enum

99

* Serializes as enum name

100

*/

101

object DayOfWeekSerializer : KSerializer<DayOfWeek>

102

```

103

104

**Usage Examples:**

105

106

```kotlin

107

import kotlinx.datetime.*

108

import kotlinx.serialization.*

109

import kotlinx.serialization.json.*

110

111

@Serializable

112

data class Event(

113

val name: String,

114

val date: LocalDate,

115

val time: LocalTime,

116

val timeZone: TimeZone,

117

val duration: DateTimePeriod

118

)

119

120

val event = Event(

121

name = "Conference",

122

date = LocalDate(2023, 12, 25),

123

time = LocalTime(15, 30),

124

timeZone = TimeZone.of("America/New_York"),

125

duration = DateTimePeriod(hours = 2, minutes = 30)

126

)

127

128

// Serialize to JSON using default serializers

129

val json = Json.encodeToString(event)

130

println(json)

131

// {"name":"Conference","date":"2023-12-25","time":"15:30:00","timeZone":"America/New_York","duration":"PT2H30M"}

132

133

// Deserialize from JSON

134

val restored = Json.decodeFromString<Event>(json)

135

println("Restored: $restored")

136

```

137

138

### ISO 8601 Serializers

139

140

Explicit ISO 8601 serializers that guarantee strict ISO compliance regardless of default `toString()` implementation.

141

142

```kotlin { .api }

143

/**

144

* Explicit ISO 8601 serializer for kotlin.time.Instant

145

* Guarantees ISO 8601 format regardless of toString implementation

146

*/

147

object InstantIso8601Serializer : KSerializer<Instant>

148

149

/**

150

* Explicit ISO 8601 serializer for LocalDate

151

* Uses ISO 8601 extended format (YYYY-MM-DD)

152

*/

153

object LocalDateIso8601Serializer : KSerializer<LocalDate>

154

155

/**

156

* Explicit ISO 8601 serializer for LocalTime

157

* Uses ISO 8601 extended format with optional fractions

158

*/

159

object LocalTimeIso8601Serializer : KSerializer<LocalTime>

160

161

/**

162

* Explicit ISO 8601 serializer for LocalDateTime

163

* Uses ISO 8601 extended format

164

*/

165

object LocalDateTimeIso8601Serializer : KSerializer<LocalDateTime>

166

167

/**

168

* Explicit ISO 8601 serializer for YearMonth

169

* Uses ISO 8601 format (YYYY-MM)

170

*/

171

object YearMonthIso8601Serializer : KSerializer<YearMonth>

172

173

/**

174

* Explicit ISO 8601 serializer for UtcOffset

175

* Uses ISO 8601 offset format

176

*/

177

object UtcOffsetIso8601Serializer : KSerializer<UtcOffset>

178

179

/**

180

* Explicit ISO 8601 serializer for DateTimePeriod

181

* Uses ISO 8601 duration format

182

*/

183

object DateTimePeriodIso8601Serializer : KSerializer<DateTimePeriod>

184

185

/**

186

* Explicit ISO 8601 serializer for DatePeriod

187

* Uses ISO 8601 date duration format

188

*/

189

object DatePeriodIso8601Serializer : KSerializer<DatePeriod>

190

```

191

192

**Usage Examples:**

193

194

```kotlin

195

import kotlinx.datetime.*

196

import kotlinx.serialization.*

197

import kotlinx.serialization.json.*

198

199

@Serializable

200

data class StrictEvent(

201

val name: String,

202

@Serializable(with = LocalDateIso8601Serializer::class)

203

val date: LocalDate,

204

@Serializable(with = LocalTimeIso8601Serializer::class)

205

val time: LocalTime,

206

@Serializable(with = DateTimePeriodIso8601Serializer::class)

207

val duration: DateTimePeriod

208

)

209

210

val event = StrictEvent(

211

name = "Meeting",

212

date = LocalDate(2023, 12, 25),

213

time = LocalTime(15, 30, 45, 123456789),

214

duration = DateTimePeriod(hours = 1, minutes = 30)

215

)

216

217

val json = Json.encodeToString(event)

218

println(json)

219

// {"name":"Meeting","date":"2023-12-25","time":"15:30:45.123456789","duration":"PT1H30M"}

220

```

221

222

### Component Serializers

223

224

Serializers that represent date/time values as JSON objects with individual component fields.

225

226

```kotlin { .api }

227

/**

228

* Component serializer for LocalDate

229

* Serializes as object: {"year": 2023, "month": 12, "day": 25}

230

*/

231

object LocalDateComponentSerializer : KSerializer<LocalDate>

232

233

/**

234

* Component serializer for LocalTime

235

* Serializes as object: {"hour": 15, "minute": 30, "second": 45, "nanosecond": 123456789}

236

*/

237

object LocalTimeComponentSerializer : KSerializer<LocalTime>

238

239

/**

240

* Component serializer for LocalDateTime

241

* Serializes as object with separate date and time components

242

*/

243

object LocalDateTimeComponentSerializer : KSerializer<LocalDateTime>

244

245

/**

246

* Component serializer for YearMonth

247

* Serializes as object: {"year": 2023, "month": 12}

248

*/

249

object YearMonthComponentSerializer : KSerializer<YearMonth>

250

251

/**

252

* Component serializer for DateTimePeriod

253

* Serializes as object with all period components

254

*/

255

object DateTimePeriodComponentSerializer : KSerializer<DateTimePeriod>

256

257

/**

258

* Component serializer for DatePeriod

259

* Serializes as object: {"years": 1, "months": 6, "days": 15}

260

*/

261

object DatePeriodComponentSerializer : KSerializer<DatePeriod>

262

```

263

264

**Usage Examples:**

265

266

```kotlin

267

import kotlinx.datetime.*

268

import kotlinx.serialization.*

269

import kotlinx.serialization.json.*

270

271

@Serializable

272

data class ComponentEvent(

273

val name: String,

274

@Serializable(with = LocalDateComponentSerializer::class)

275

val date: LocalDate,

276

@Serializable(with = LocalTimeComponentSerializer::class)

277

val time: LocalTime,

278

@Serializable(with = DatePeriodComponentSerializer::class)

279

val duration: DatePeriod

280

)

281

282

val event = ComponentEvent(

283

name = "Workshop",

284

date = LocalDate(2023, 12, 25),

285

time = LocalTime(15, 30, 45),

286

duration = DatePeriod(days = 2)

287

)

288

289

val json = Json { prettyPrint = true }.encodeToString(event)

290

println(json)

291

/*

292

{

293

"name": "Workshop",

294

"date": {

295

"year": 2023,

296

"month": 12,

297

"day": 25

298

},

299

"time": {

300

"hour": 15,

301

"minute": 30,

302

"second": 45,

303

"nanosecond": 0

304

},

305

"duration": {

306

"years": 0,

307

"months": 0,

308

"days": 2

309

}

310

}

311

*/

312

313

// Deserialize back to objects

314

val restored = Json.decodeFromString<ComponentEvent>(json)

315

println("Restored date: ${restored.date}") // 2023-12-25

316

```

317

318

### Specialized DateTimeUnit Serializers

319

320

Serializers for different DateTimeUnit types based on their classification.

321

322

```kotlin { .api }

323

/**

324

* Serializer for TimeBased DateTimeUnit

325

* Handles units with nanosecond precision

326

*/

327

object TimeBasedDateTimeUnitSerializer : KSerializer<DateTimeUnit.TimeBased>

328

329

/**

330

* Serializer for DayBased DateTimeUnit

331

* Handles day and week units

332

*/

333

object DayBasedDateTimeUnitSerializer : KSerializer<DateTimeUnit.DayBased>

334

335

/**

336

* Serializer for MonthBased DateTimeUnit

337

* Handles month, quarter, year, and century units

338

*/

339

object MonthBasedDateTimeUnitSerializer : KSerializer<DateTimeUnit.MonthBased>

340

341

/**

342

* Serializer for DateBased DateTimeUnit (base class)

343

* Handles all date-based units

344

*/

345

object DateBasedDateTimeUnitSerializer : KSerializer<DateTimeUnit.DateBased>

346

```

347

348

**Usage Examples:**

349

350

```kotlin

351

import kotlinx.datetime.*

352

import kotlinx.serialization.*

353

import kotlinx.serialization.json.*

354

355

@Serializable

356

data class ScheduleRule(

357

val name: String,

358

@Serializable(with = TimeBasedDateTimeUnitSerializer::class)

359

val timeUnit: DateTimeUnit.TimeBased,

360

@Serializable(with = MonthBasedDateTimeUnitSerializer::class)

361

val dateUnit: DateTimeUnit.MonthBased,

362

val interval: Int

363

)

364

365

val rule = ScheduleRule(

366

name = "Quarterly Review",

367

timeUnit = DateTimeUnit.HOUR,

368

dateUnit = DateTimeUnit.QUARTER,

369

interval = 1

370

)

371

372

val json = Json.encodeToString(rule)

373

println(json)

374

// Exact format depends on implementation - could be unit names or nanosecond values

375

```

376

377

## Advanced Serialization Patterns

378

379

### Custom Serialization Context

380

381

```kotlin

382

import kotlinx.datetime.*

383

import kotlinx.serialization.*

384

import kotlinx.serialization.json.*

385

import kotlinx.serialization.modules.*

386

387

// Custom serializer that includes time zone information

388

@Serializer(forClass = LocalDateTime::class)

389

object LocalDateTimeWithZoneSerializer : KSerializer<LocalDateTime> {

390

override val descriptor = buildClassSerialDescriptor("LocalDateTimeWithZone") {

391

element<String>("dateTime")

392

element<String>("timeZone")

393

}

394

395

override fun serialize(encoder: Encoder, value: LocalDateTime) {

396

encoder.encodeStructure(descriptor) {

397

encodeStringElement(descriptor, 0, value.toString())

398

// Would need context to get time zone - this is a conceptual example

399

encodeStringElement(descriptor, 1, "UTC")

400

}

401

}

402

403

override fun deserialize(decoder: Decoder): LocalDateTime {

404

return decoder.decodeStructure(descriptor) {

405

var dateTime: String? = null

406

var timeZone: String? = null

407

408

while (true) {

409

when (val index = decodeElementIndex(descriptor)) {

410

0 -> dateTime = decodeStringElement(descriptor, 0)

411

1 -> timeZone = decodeStringElement(descriptor, 1)

412

CompositeDecoder.DECODE_DONE -> break

413

else -> error("Unexpected index: $index")

414

}

415

}

416

417

LocalDateTime.parse(dateTime!!)

418

}

419

}

420

}

421

422

// Using custom module

423

val customModule = SerializersModule {

424

contextual(LocalDateTime::class, LocalDateTimeWithZoneSerializer)

425

}

426

427

val json = Json { serializersModule = customModule }

428

```

429

430

### Conditional Serialization

431

432

```kotlin

433

import kotlinx.datetime.*

434

import kotlinx.serialization.*

435

import kotlinx.serialization.json.*

436

437

@Serializable

438

data class FlexibleEvent(

439

val name: String,

440

val date: LocalDate,

441

val time: LocalTime? = null, // Optional time

442

@Serializable(with = DateTimePeriodSerializer::class)

443

val duration: DateTimePeriod? = null // Optional duration

444

) {

445

// Custom property for all-day events

446

val isAllDay: Boolean get() = time == null

447

448

// Convert to full datetime if time is present

449

val dateTime: LocalDateTime? get() = time?.let { date.atTime(it) }

450

}

451

452

val allDayEvent = FlexibleEvent(

453

name = "Holiday",

454

date = LocalDate(2023, 12, 25)

455

)

456

457

val timedEvent = FlexibleEvent(

458

name = "Meeting",

459

date = LocalDate(2023, 12, 25),

460

time = LocalTime(15, 30),

461

duration = DateTimePeriod(hours = 2)

462

)

463

464

// Both serialize differently

465

println("All-day: ${Json.encodeToString(allDayEvent)}")

466

println("Timed: ${Json.encodeToString(timedEvent)}")

467

```

468

469

### Polymorphic Serialization

470

471

```kotlin

472

import kotlinx.datetime.*

473

import kotlinx.serialization.*

474

import kotlinx.serialization.json.*

475

476

@Serializable

477

sealed class TimeSpan {

478

@Serializable

479

@SerialName("duration")

480

data class DurationSpan(

481

@Serializable(with = DateTimePeriodSerializer::class)

482

val period: DateTimePeriod

483

) : TimeSpan()

484

485

@Serializable

486

@SerialName("range")

487

data class DateRangeSpan(

488

val start: LocalDate,

489

val end: LocalDate

490

) : TimeSpan()

491

492

@Serializable

493

@SerialName("recurring")

494

data class RecurringSpan(

495

val start: LocalDate,

496

@Serializable(with = DateTimePeriodSerializer::class)

497

val interval: DateTimePeriod,

498

val count: Int

499

) : TimeSpan()

500

}

501

502

@Serializable

503

data class Task(

504

val name: String,

505

val timeSpan: TimeSpan

506

)

507

508

val tasks = listOf(

509

Task("Quick task", TimeSpan.DurationSpan(DateTimePeriod(hours = 1))),

510

Task("Project", TimeSpan.DateRangeSpan(

511

LocalDate(2023, 12, 1),

512

LocalDate(2023, 12, 31)

513

)),

514

Task("Weekly meeting", TimeSpan.RecurringSpan(

515

LocalDate(2023, 12, 1),

516

DateTimePeriod(days = 7),

517

count = 4

518

))

519

)

520

521

val json = Json { prettyPrint = true }

522

tasks.forEach { task ->

523

println("${task.name}:")

524

println(json.encodeToString(task))

525

println()

526

}

527

```

528

529

### Database Serialization

530

531

```kotlin

532

import kotlinx.datetime.*

533

import kotlinx.serialization.*

534

535

// Example adapter for database storage

536

class DateTimeSerializationAdapter {

537

538

// Convert to database-friendly formats

539

fun localDateToLong(date: LocalDate): Long = date.toEpochDays()

540

fun longToLocalDate(epochDays: Long): LocalDate = LocalDate.fromEpochDays(epochDays)

541

542

fun instantToLong(instant: Instant): Long = instant.epochSeconds

543

fun longToInstant(epochSeconds: Long): Instant = Instant.fromEpochSeconds(epochSeconds)

544

545

fun timeZoneToString(timeZone: TimeZone): String = timeZone.id

546

fun stringToTimeZone(id: String): TimeZone = TimeZone.of(id)

547

548

fun periodToString(period: DateTimePeriod): String = period.toString()

549

fun stringToPeriod(str: String): DateTimePeriod = DateTimePeriod.parse(str)

550

}

551

552

// Usage in data classes for database entities

553

@Serializable

554

data class DatabaseEvent(

555

val id: Long,

556

val name: String,

557

558

// Store as epoch days for efficient database operations

559

@Serializable(with = EpochDaysSerializer::class)

560

val date: LocalDate,

561

562

// Store as epoch seconds

563

@Serializable(with = EpochSecondsSerializer::class)

564

val createdAt: Instant,

565

566

// Store time zone as string ID

567

val timeZoneId: String

568

) {

569

// Computed property for actual time zone

570

val timeZone: TimeZone get() = TimeZone.of(timeZoneId)

571

}

572

573

// Custom serializers for database storage

574

object EpochDaysSerializer : KSerializer<LocalDate> {

575

override val descriptor = PrimitiveSerialDescriptor("EpochDays", PrimitiveKind.LONG)

576

577

override fun serialize(encoder: Encoder, value: LocalDate) {

578

encoder.encodeLong(value.toEpochDays())

579

}

580

581

override fun deserialize(decoder: Decoder): LocalDate {

582

return LocalDate.fromEpochDays(decoder.decodeLong())

583

}

584

}

585

586

object EpochSecondsSerializer : KSerializer<Instant> {

587

override val descriptor = PrimitiveSerialDescriptor("EpochSeconds", PrimitiveKind.LONG)

588

589

override fun serialize(encoder: Encoder, value: Instant) {

590

encoder.encodeLong(value.epochSeconds)

591

}

592

593

override fun deserialize(decoder: Decoder): Instant {

594

return Instant.fromEpochSeconds(decoder.decodeLong())

595

}

596

}

597

```

598

599

## Migration and Compatibility

600

601

### Handling Format Changes

602

603

```kotlin

604

import kotlinx.datetime.*

605

import kotlinx.serialization.*

606

import kotlinx.serialization.json.*

607

608

// Versioned serialization for backward compatibility

609

@Serializable

610

data class EventV1(

611

val name: String,

612

val dateString: String // Old format: stored as string

613

)

614

615

@Serializable

616

data class EventV2(

617

val name: String,

618

val date: LocalDate, // New format: proper LocalDate

619

val version: Int = 2

620

)

621

622

// Migration serializer

623

object EventMigrationSerializer : KSerializer<EventV2> {

624

override val descriptor = EventV2.serializer().descriptor

625

626

override fun serialize(encoder: Encoder, value: EventV2) {

627

EventV2.serializer().serialize(encoder, value)

628

}

629

630

override fun deserialize(decoder: Decoder): EventV2 {

631

val element = decoder.decodeSerializableValue(JsonElement.serializer())

632

val jsonObject = element.jsonObject

633

634

return when {

635

"version" in jsonObject -> {

636

// New format

637

Json.decodeFromJsonElement(EventV2.serializer(), element)

638

}

639

"dateString" in jsonObject -> {

640

// Old format - migrate

641

val v1 = Json.decodeFromJsonElement(EventV1.serializer(), element)

642

EventV2(

643

name = v1.name,

644

date = LocalDate.parse(v1.dateString) // Convert string to LocalDate

645

)

646

}

647

else -> error("Unknown event format")

648

}

649

}

650

}

651

652

// Usage

653

val oldJson = """{"name":"Meeting","dateString":"2023-12-25"}"""

654

val newJson = """{"name":"Conference","date":"2023-12-31","version":2}"""

655

656

val event1 = Json.decodeFromString(EventMigrationSerializer, oldJson)

657

val event2 = Json.decodeFromString(EventMigrationSerializer, newJson)

658

659

println("Migrated: $event1") // EventV2 with LocalDate

660

println("Current: $event2") // EventV2 as-is

661

```

662

663

### Error Handling and Validation

664

665

```kotlin

666

import kotlinx.datetime.*

667

import kotlinx.serialization.*

668

import kotlinx.serialization.json.*

669

670

// Robust serializer with validation

671

object ValidatedLocalDateSerializer : KSerializer<LocalDate> {

672

override val descriptor = PrimitiveSerialDescriptor("ValidatedLocalDate", PrimitiveKind.STRING)

673

674

override fun serialize(encoder: Encoder, value: LocalDate) {

675

encoder.encodeString(value.toString())

676

}

677

678

override fun deserialize(decoder: Decoder): LocalDate {

679

val dateString = decoder.decodeString()

680

681

return try {

682

LocalDate.parse(dateString)

683

} catch (e: Exception) {

684

// Try alternative formats or provide default

685

when {

686

dateString.matches(Regex("""\d{8}""")) -> {

687

// Handle YYYYMMDD format

688

val year = dateString.substring(0, 4).toInt()

689

val month = dateString.substring(4, 6).toInt()

690

val day = dateString.substring(6, 8).toInt()

691

LocalDate(year, month, day)

692

}

693

else -> throw SerializationException("Invalid date format: $dateString", e)

694

}

695

}

696

}

697

}

698

699

// Safe parsing wrapper

700

inline fun <reified T> safeJsonDecode(json: String): Result<T> {

701

return try {

702

Result.success(Json.decodeFromString<T>(json))

703

} catch (e: SerializationException) {

704

Result.failure(e)

705

}

706

}

707

708

// Usage

709

val validJson = """{"date":"2023-12-25"}"""

710

val invalidJson = """{"date":"invalid-date"}"""

711

val alternativeJson = """{"date":"20231225"}"""

712

713

@Serializable

714

data class DateContainer(@Serializable(with = ValidatedLocalDateSerializer::class) val date: LocalDate)

715

716

val result1 = safeJsonDecode<DateContainer>(validJson) // Success

717

val result2 = safeJsonDecode<DateContainer>(invalidJson) // Failure

718

val result3 = safeJsonDecode<DateContainer>(alternativeJson) // Success (handled YYYYMMDD)

719

720

result1.onSuccess { println("Valid: ${it.date}") }

721

result2.onFailure { println("Error: ${it.message}") }

722

result3.onSuccess { println("Alternative: ${it.date}") }

723

```