or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdbuiltins.mddescriptors.mdencoding.mdindex.mdmodules.mdserializers.md

encoding.mddocs/

0

# Encoding and Decoding

1

2

Complete reference for the low-level encoding and decoding APIs used by format implementors in kotlinx.serialization-core-js.

3

4

## Core Encoding Interfaces

5

6

### Encoder

7

8

Primary interface for encoding values into a specific format.

9

10

```kotlin

11

interface Encoder {

12

val serializersModule: SerializersModule

13

14

fun encodeBoolean(value: Boolean)

15

fun encodeByte(value: Byte)

16

fun encodeChar(value: Char)

17

fun encodeShort(value: Short)

18

fun encodeInt(value: Int)

19

fun encodeLong(value: Long)

20

fun encodeFloat(value: Float)

21

fun encodeDouble(value: Double)

22

fun encodeString(value: String)

23

24

fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)

25

fun encodeNull()

26

fun encodeNotNullMark()

27

fun encodeInline(descriptor: SerialDescriptor): Encoder

28

29

fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder

30

fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder

31

32

fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T)

33

fun <T : Any> encodeNullableSerializableValue(serializer: SerializationStrategy<T>, value: T?)

34

}

35

```

36

{ .api }

37

38

**Usage:**

39

40

```javascript

41

class JsonEncoder {

42

constructor(serializersModule) {

43

this.serializersModule = serializersModule;

44

this.output = "";

45

}

46

47

encodeString(value) {

48

this.output += `"${this.escapeString(value)}"`;

49

}

50

51

encodeInt(value) {

52

this.output += value.toString();

53

}

54

55

encodeBoolean(value) {

56

this.output += value ? "true" : "false";

57

}

58

59

encodeNull() {

60

this.output += "null";

61

}

62

63

beginStructure(descriptor) {

64

this.output += "{";

65

return new JsonCompositeEncoder(this);

66

}

67

68

beginCollection(descriptor, collectionSize) {

69

this.output += "[";

70

return new JsonCompositeEncoder(this);

71

}

72

73

escapeString(str) {

74

return str.replace(/\\/g, "\\\\")

75

.replace(/"/g, '\\"')

76

.replace(/\n/g, "\\n");

77

}

78

}

79

```

80

81

### CompositeEncoder

82

83

Interface for encoding structured data (objects, arrays, maps).

84

85

```kotlin

86

interface CompositeEncoder {

87

val serializersModule: SerializersModule

88

89

fun endStructure(descriptor: SerialDescriptor)

90

91

fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean

92

93

fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean)

94

fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte)

95

fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char)

96

fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short)

97

fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int)

98

fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long)

99

fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float)

100

fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double)

101

fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String)

102

103

fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder

104

fun <T> encodeSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T)

105

fun <T : Any> encodeNullableSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T?)

106

}

107

```

108

{ .api }

109

110

**Usage:**

111

112

```javascript

113

class JsonCompositeEncoder {

114

constructor(encoder) {

115

this.encoder = encoder;

116

this.serializersModule = encoder.serializersModule;

117

this.elementCount = 0;

118

}

119

120

encodeBooleanElement(descriptor, index, value) {

121

this.writeElementSeparator();

122

this.writeElementName(descriptor, index);

123

this.encoder.encodeBoolean(value);

124

}

125

126

encodeStringElement(descriptor, index, value) {

127

this.writeElementSeparator();

128

this.writeElementName(descriptor, index);

129

this.encoder.encodeString(value);

130

}

131

132

endStructure(descriptor) {

133

if (descriptor.kind === StructureKind.CLASS) {

134

this.encoder.output += "}";

135

} else if (descriptor.kind === StructureKind.LIST) {

136

this.encoder.output += "]";

137

}

138

}

139

140

writeElementSeparator() {

141

if (this.elementCount++ > 0) {

142

this.encoder.output += ",";

143

}

144

}

145

146

writeElementName(descriptor, index) {

147

if (descriptor.kind === StructureKind.CLASS) {

148

const name = descriptor.getElementName(index);

149

this.encoder.output += `"${name}":`;

150

}

151

}

152

153

shouldEncodeElementDefault(descriptor, index) {

154

return true; // Always encode defaults in this example

155

}

156

}

157

```

158

159

## Core Decoding Interfaces

160

161

### Decoder

162

163

Primary interface for decoding values from a specific format.

164

165

```kotlin

166

interface Decoder {

167

val serializersModule: SerializersModule

168

169

fun decodeBoolean(): Boolean

170

fun decodeByte(): Byte

171

fun decodeChar(): Char

172

fun decodeShort(): Short

173

fun decodeInt(): Int

174

fun decodeLong(): Long

175

fun decodeFloat(): Float

176

fun decodeDouble(): Double

177

fun decodeString(): String

178

179

fun decodeEnum(enumDescriptor: SerialDescriptor): Int

180

fun decodeNull(): Nothing?

181

fun decodeNotNullMark(): Boolean

182

fun decodeInline(descriptor: SerialDescriptor): Decoder

183

184

fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder

185

186

fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T

187

fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T>): T?

188

}

189

```

190

{ .api }

191

192

**Usage:**

193

194

```javascript

195

class JsonDecoder {

196

constructor(input, serializersModule) {

197

this.input = input;

198

this.serializersModule = serializersModule;

199

this.position = 0;

200

}

201

202

decodeString() {

203

this.skipWhitespace();

204

if (this.input[this.position] !== '"') {

205

throw new Error("Expected '\"' at position " + this.position);

206

}

207

208

this.position++; // Skip opening quote

209

const start = this.position;

210

211

while (this.position < this.input.length && this.input[this.position] !== '"') {

212

if (this.input[this.position] === '\\') {

213

this.position++; // Skip escape character

214

}

215

this.position++;

216

}

217

218

const value = this.input.substring(start, this.position);

219

this.position++; // Skip closing quote

220

return this.unescapeString(value);

221

}

222

223

decodeInt() {

224

this.skipWhitespace();

225

const start = this.position;

226

227

if (this.input[this.position] === '-') {

228

this.position++;

229

}

230

231

while (this.position < this.input.length &&

232

this.input[this.position] >= '0' &&

233

this.input[this.position] <= '9') {

234

this.position++;

235

}

236

237

const value = this.input.substring(start, this.position);

238

return parseInt(value, 10);

239

}

240

241

decodeBoolean() {

242

this.skipWhitespace();

243

if (this.input.substring(this.position, this.position + 4) === "true") {

244

this.position += 4;

245

return true;

246

} else if (this.input.substring(this.position, this.position + 5) === "false") {

247

this.position += 5;

248

return false;

249

} else {

250

throw new Error("Expected boolean at position " + this.position);

251

}

252

}

253

254

beginStructure(descriptor) {

255

this.skipWhitespace();

256

257

if (descriptor.kind === StructureKind.CLASS) {

258

this.expectChar('{');

259

} else if (descriptor.kind === StructureKind.LIST) {

260

this.expectChar('[');

261

}

262

263

return new JsonCompositeDecoder(this, descriptor);

264

}

265

266

skipWhitespace() {

267

while (this.position < this.input.length &&

268

/\s/.test(this.input[this.position])) {

269

this.position++;

270

}

271

}

272

273

expectChar(expected) {

274

if (this.input[this.position] !== expected) {

275

throw new Error(`Expected '${expected}' at position ${this.position}`);

276

}

277

this.position++;

278

}

279

}

280

```

281

282

### CompositeDecoder

283

284

Interface for decoding structured data with special constants for control flow.

285

286

```kotlin

287

interface CompositeDecoder {

288

val serializersModule: SerializersModule

289

290

fun endStructure(descriptor: SerialDescriptor)

291

fun decodeElementIndex(descriptor: SerialDescriptor): Int

292

293

fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean

294

fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte

295

fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char

296

fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short

297

fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int

298

fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long

299

fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float

300

fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double

301

fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String

302

303

fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder

304

fun <T> decodeSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T

305

fun <T : Any> decodeNullableSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T?

306

307

companion object {

308

const val DECODE_DONE = -1

309

const val UNKNOWN_NAME = -3

310

}

311

}

312

```

313

{ .api }

314

315

**Usage:**

316

317

```javascript

318

class JsonCompositeDecoder {

319

constructor(decoder, descriptor) {

320

this.decoder = decoder;

321

this.serializersModule = decoder.serializersModule;

322

this.descriptor = descriptor;

323

this.elementIndex = 0;

324

this.namesRead = new Set();

325

}

326

327

decodeElementIndex(descriptor) {

328

this.decoder.skipWhitespace();

329

330

// Check for end of structure

331

if ((descriptor.kind === StructureKind.CLASS && this.decoder.input[this.decoder.position] === '}') ||

332

(descriptor.kind === StructureKind.LIST && this.decoder.input[this.decoder.position] === ']')) {

333

return CompositeDecoder.DECODE_DONE;

334

}

335

336

// Handle comma separator

337

if (this.elementIndex > 0) {

338

this.decoder.expectChar(',');

339

this.decoder.skipWhitespace();

340

}

341

342

if (descriptor.kind === StructureKind.CLASS) {

343

// Read property name

344

const name = this.decoder.decodeString();

345

this.decoder.skipWhitespace();

346

this.decoder.expectChar(':');

347

348

const index = descriptor.getElementIndex(name);

349

if (index === CompositeDecoder.UNKNOWN_NAME) {

350

// Skip unknown property

351

this.skipValue();

352

return this.decodeElementIndex(descriptor);

353

}

354

355

this.elementIndex++;

356

return index;

357

} else if (descriptor.kind === StructureKind.LIST) {

358

// For arrays, return sequential index

359

return this.elementIndex++;

360

}

361

362

return CompositeDecoder.DECODE_DONE;

363

}

364

365

decodeStringElement(descriptor, index) {

366

return this.decoder.decodeString();

367

}

368

369

decodeIntElement(descriptor, index) {

370

return this.decoder.decodeInt();

371

}

372

373

endStructure(descriptor) {

374

this.decoder.skipWhitespace();

375

376

if (descriptor.kind === StructureKind.CLASS) {

377

this.decoder.expectChar('}');

378

} else if (descriptor.kind === StructureKind.LIST) {

379

this.decoder.expectChar(']');

380

}

381

}

382

383

skipValue() {

384

// Implementation to skip JSON value

385

this.decoder.skipWhitespace();

386

const ch = this.decoder.input[this.decoder.position];

387

388

if (ch === '"') {

389

this.decoder.decodeString();

390

} else if (ch === '{') {

391

this.skipObject();

392

} else if (ch === '[') {

393

this.skipArray();

394

} else {

395

this.skipPrimitive();

396

}

397

}

398

}

399

```

400

401

## Inline Extension Functions

402

403

### Encoder Extensions

404

405

Convenient inline functions for encoding structures and collections.

406

407

```kotlin

408

inline fun <T> Encoder.encodeStructure(

409

descriptor: SerialDescriptor,

410

crossinline block: CompositeEncoder.() -> T

411

): T

412

413

inline fun <T> Encoder.encodeCollection(

414

descriptor: SerialDescriptor,

415

collectionSize: Int,

416

crossinline block: CompositeEncoder.() -> T

417

): T

418

419

inline fun <T> Encoder.encodeCollection(

420

descriptor: SerialDescriptor,

421

collection: Collection<*>,

422

crossinline block: CompositeEncoder.() -> T

423

): T

424

```

425

{ .api }

426

427

**Usage:**

428

429

```javascript

430

// Custom serializer using encodeStructure

431

class PersonSerializer {

432

constructor() {

433

this.descriptor = buildClassSerialDescriptor("Person") {

434

element("name", String.serializer().descriptor)

435

element("age", Int.serializer().descriptor)

436

};

437

}

438

439

serialize(encoder, value) {

440

encoder.encodeStructure(this.descriptor) {

441

encodeStringElement(this.descriptor, 0, value.name)

442

encodeIntElement(this.descriptor, 1, value.age)

443

};

444

}

445

}

446

447

// Custom collection serializer

448

class CustomListSerializer {

449

constructor(elementSerializer) {

450

this.elementSerializer = elementSerializer;

451

this.descriptor = listSerialDescriptor(elementSerializer.descriptor);

452

}

453

454

serialize(encoder, value) {

455

encoder.encodeCollection(this.descriptor, value.length) {

456

value.forEach((element, index) => {

457

encodeSerializableElement(this.descriptor, index, this.elementSerializer, element)

458

})

459

};

460

}

461

}

462

```

463

464

### Decoder Extensions

465

466

Convenient inline function for decoding structures.

467

468

```kotlin

469

inline fun <T> Decoder.decodeStructure(

470

descriptor: SerialDescriptor,

471

crossinline block: CompositeDecoder.() -> T

472

): T

473

```

474

{ .api }

475

476

**Usage:**

477

478

```javascript

479

class PersonSerializer {

480

deserialize(decoder) {

481

return decoder.decodeStructure(this.descriptor) {

482

let name = "";

483

let age = 0;

484

485

while (true) {

486

const index = decodeElementIndex(this.descriptor);

487

if (index === CompositeDecoder.DECODE_DONE) break;

488

489

switch (index) {

490

case 0:

491

name = decodeStringElement(this.descriptor, 0);

492

break;

493

case 1:

494

age = decodeIntElement(this.descriptor, 1);

495

break;

496

default:

497

throw new SerializationException(`Unexpected index: ${index}`);

498

}

499

}

500

501

return new Person(name, age);

502

};

503

}

504

}

505

```

506

507

## Abstract Base Classes

508

509

### AbstractEncoder

510

511

Base implementation providing common encoder functionality (Experimental).

512

513

```kotlin

514

@ExperimentalSerializationApi

515

abstract class AbstractEncoder : Encoder, CompositeEncoder {

516

override fun encodeInline(descriptor: SerialDescriptor): Encoder = this

517

override fun encodeNotNullMark() {}

518

override fun encodeNull() = throw SerializationException("null is not supported")

519

520

// Default implementations for composite encoding

521

override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) = encodeBoolean(value)

522

override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) = encodeByte(value)

523

// ... similar for other primitive types

524

525

override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = true

526

527

override fun <T> encodeSerializableElement(descriptor: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T) {

528

serializer.serialize(this, value)

529

}

530

}

531

```

532

{ .api }

533

534

**Usage:**

535

536

```javascript

537

class CustomEncoder extends AbstractEncoder {

538

constructor(output, serializersModule = EmptySerializersModule()) {

539

super();

540

this.output = output;

541

this.serializersModule = serializersModule;

542

}

543

544

encodeString(value) {

545

this.output.write(`"${value}"`);

546

}

547

548

encodeInt(value) {

549

this.output.write(value.toString());

550

}

551

552

beginStructure(descriptor) {

553

this.output.write('{');

554

return this;

555

}

556

557

endStructure(descriptor) {

558

this.output.write('}');

559

}

560

561

// Inherit default implementations for element encoding

562

}

563

```

564

565

### AbstractDecoder

566

567

Base implementation providing common decoder functionality (Experimental).

568

569

```kotlin

570

@ExperimentalSerializationApi

571

abstract class AbstractDecoder : Decoder, CompositeDecoder {

572

override fun decodeInline(descriptor: SerialDescriptor): Decoder = this

573

override fun decodeNotNullMark(): Boolean = true

574

575

override fun decodeElementIndex(descriptor: SerialDescriptor): Int = CompositeDecoder.DECODE_DONE

576

577

// Default implementations for composite decoding

578

override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeBoolean()

579

override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeByte()

580

// ... similar for other primitive types

581

582

override fun <T> decodeSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy<T>): T {

583

return deserializer.deserialize(this)

584

}

585

}

586

```

587

{ .api }

588

589

**Usage:**

590

591

```javascript

592

class CustomDecoder extends AbstractDecoder {

593

constructor(input, serializersModule = EmptySerializersModule()) {

594

super();

595

this.input = input;

596

this.position = 0;

597

this.serializersModule = serializersModule;

598

}

599

600

decodeString() {

601

// Implementation to read string from input

602

return this.readQuotedString();

603

}

604

605

decodeInt() {

606

// Implementation to read integer from input

607

return this.readNumber();

608

}

609

610

beginStructure(descriptor) {

611

this.skipChar('{');

612

return this;

613

}

614

615

endStructure(descriptor) {

616

this.skipChar('}');

617

}

618

619

// Inherit default implementations for element decoding

620

}

621

```

622

623

## Specialized Interfaces

624

625

### ChunkedDecoder

626

627

Interface for efficient decoding of large strings in chunks (Experimental).

628

629

```kotlin

630

@ExperimentalSerializationApi

631

interface ChunkedDecoder {

632

fun decodeStringChunked(consumeChunk: (chunk: String) -> Unit): Boolean

633

}

634

```

635

{ .api }

636

637

**Usage:**

638

639

```javascript

640

class StreamingJsonDecoder extends AbstractDecoder {

641

decodeStringChunked(consumeChunk) {

642

const bufferSize = 1024;

643

let totalChunks = 0;

644

645

while (this.hasMoreData()) {

646

const chunk = this.readChunk(bufferSize);

647

consumeChunk(chunk);

648

totalChunks++;

649

}

650

651

return totalChunks > 0;

652

}

653

654

// Use for large string processing

655

decodeString() {

656

let result = "";

657

658

this.decodeStringChunked(chunk => {

659

result += chunk;

660

});

661

662

return result;

663

}

664

}

665

```

666

667

## Format Implementation Example

668

669

Here's a complete minimal format implementation:

670

671

```javascript

672

class SimpleFormat {

673

constructor(serializersModule = EmptySerializersModule()) {

674

this.serializersModule = serializersModule;

675

}

676

677

encodeToString(serializer, value) {

678

const encoder = new SimpleEncoder(this.serializersModule);

679

serializer.serialize(encoder, value);

680

return encoder.getResult();

681

}

682

683

decodeFromString(serializer, string) {

684

const decoder = new SimpleDecoder(string, this.serializersModule);

685

return serializer.deserialize(decoder);

686

}

687

}

688

689

class SimpleEncoder extends AbstractEncoder {

690

constructor(serializersModule) {

691

super();

692

this.serializersModule = serializersModule;

693

this.result = [];

694

}

695

696

encodeString(value) {

697

this.result.push(`S:${value}`);

698

}

699

700

encodeInt(value) {

701

this.result.push(`I:${value}`);

702

}

703

704

beginStructure(descriptor) {

705

this.result.push(`{${descriptor.serialName}`);

706

return this;

707

}

708

709

endStructure(descriptor) {

710

this.result.push('}');

711

}

712

713

getResult() {

714

return this.result.join(',');

715

}

716

}

717

718

class SimpleDecoder extends AbstractDecoder {

719

constructor(input, serializersModule) {

720

super();

721

this.serializersModule = serializersModule;

722

this.tokens = input.split(',');

723

this.position = 0;

724

}

725

726

decodeString() {

727

const token = this.tokens[this.position++];

728

if (!token.startsWith('S:')) {

729

throw new Error('Expected string token');

730

}

731

return token.substring(2);

732

}

733

734

decodeInt() {

735

const token = this.tokens[this.position++];

736

if (!token.startsWith('I:')) {

737

throw new Error('Expected int token');

738

}

739

return parseInt(token.substring(2), 10);

740

}

741

742

beginStructure(descriptor) {

743

const token = this.tokens[this.position++];

744

if (!token.startsWith('{')) {

745

throw new Error('Expected structure start');

746

}

747

return this;

748

}

749

750

endStructure(descriptor) {

751

const token = this.tokens[this.position++];

752

if (token !== '}') {

753

throw new Error('Expected structure end');

754

}

755

}

756

}

757

758

// Usage

759

const format = new SimpleFormat();

760

const user = new User("John", 30);

761

const encoded = format.encodeToString(User.serializer(), user);

762

const decoded = format.decodeFromString(User.serializer(), encoded);

763

```

764

765

The encoding/decoding APIs provide the foundation for implementing any serialization format while maintaining compatibility with the kotlinx.serialization ecosystem.