0
# JSON Handling
1
2
Comprehensive JSON parsing and validation with proper type definitions for all JSON value types. These codecs provide safe parsing and serialization of JSON data with full type safety.
3
4
## Capabilities
5
6
### JSON Type Definitions
7
8
Complete type definitions for all possible JSON values, enabling type-safe JSON manipulation.
9
10
```typescript { .api }
11
/**
12
* Interface for JSON objects with string keys
13
*/
14
interface JsonRecord {
15
readonly [key: string]: Json;
16
}
17
18
/**
19
* Interface for JSON arrays extending ReadonlyArray
20
*/
21
interface JsonArray extends ReadonlyArray<Json> {}
22
23
/**
24
* Union type representing any valid JSON value
25
* Includes all primitive JSON types and recursive structures
26
*/
27
type Json = boolean | number | string | null | JsonArray | JsonRecord;
28
```
29
30
### JSON Array Codec
31
32
Codec for validating and working with JSON arrays specifically.
33
34
```typescript { .api }
35
/**
36
* Codec for JSON arrays
37
* Validates arrays where all elements are valid JSON values
38
*/
39
const JsonArray: t.Type<JsonArray>;
40
```
41
42
**Usage Examples:**
43
44
```typescript
45
import { JsonArray } from "io-ts-types";
46
47
const result1 = JsonArray.decode([1, "hello", true, null]);
48
// Right([1, "hello", true, null] as JsonArray)
49
50
const result2 = JsonArray.decode([1, [2, 3], { key: "value" }]);
51
// Right([1, [2, 3], { key: "value" }] as JsonArray)
52
53
const result3 = JsonArray.decode([]);
54
// Right([] as JsonArray) - empty array is valid
55
56
const result4 = JsonArray.decode([1, undefined, 3]);
57
// Left([ValidationError]) - undefined is not a valid JSON value
58
59
const result5 = JsonArray.decode("not-an-array");
60
// Left([ValidationError]) - not an array
61
62
// Encoding is identity for valid JSON arrays
63
const encoded = JsonArray.encode([1, "test", null]);
64
// [1, "test", null]
65
```
66
67
### JSON Record Codec
68
69
Codec for validating and working with JSON objects specifically.
70
71
```typescript { .api }
72
/**
73
* Codec for JSON objects (records)
74
* Validates objects where all values are valid JSON values
75
*/
76
const JsonRecord: t.Type<JsonRecord>;
77
```
78
79
**Usage Examples:**
80
81
```typescript
82
import { JsonRecord } from "io-ts-types";
83
84
const result1 = JsonRecord.decode({
85
name: "Alice",
86
age: 25,
87
active: true,
88
metadata: null
89
});
90
// Right({ name: "Alice", age: 25, active: true, metadata: null } as JsonRecord)
91
92
const result2 = JsonRecord.decode({
93
users: [{ id: 1, name: "Bob" }],
94
config: { enabled: true, timeout: 30 }
95
});
96
// Right({ users: [...], config: {...} } as JsonRecord)
97
98
const result3 = JsonRecord.decode({});
99
// Right({} as JsonRecord) - empty object is valid
100
101
const result4 = JsonRecord.decode({
102
valid: "value",
103
invalid: undefined
104
});
105
// Left([ValidationError]) - undefined is not a valid JSON value
106
107
const result5 = JsonRecord.decode([1, 2, 3]);
108
// Left([ValidationError]) - array is not a record
109
110
// Encoding is identity for valid JSON records
111
const encoded = JsonRecord.encode({ key: "value", number: 42 });
112
// { key: "value", number: 42 }
113
```
114
115
### JSON Value Codec
116
117
Codec for any valid JSON value, providing comprehensive JSON validation.
118
119
```typescript { .api }
120
/**
121
* Codec for any valid JSON value
122
* Validates any value that can be represented in JSON
123
*/
124
const Json: t.Type<Json>;
125
```
126
127
**Usage Examples:**
128
129
```typescript
130
import { Json } from "io-ts-types";
131
132
// Primitive JSON values
133
const result1 = Json.decode(42);
134
// Right(42 as Json)
135
136
const result2 = Json.decode("hello");
137
// Right("hello" as Json)
138
139
const result3 = Json.decode(true);
140
// Right(true as Json)
141
142
const result4 = Json.decode(null);
143
// Right(null as Json)
144
145
// Complex JSON structures
146
const result5 = Json.decode({
147
name: "Alice",
148
scores: [95, 87, 92],
149
metadata: {
150
created: "2023-12-25",
151
tags: ["student", "honor-roll"]
152
}
153
});
154
// Right({...} as Json)
155
156
// Invalid JSON values
157
const result6 = Json.decode(undefined);
158
// Left([ValidationError]) - undefined not valid in JSON
159
160
const result7 = Json.decode(Symbol("test"));
161
// Left([ValidationError]) - symbols not valid in JSON
162
163
const result8 = Json.decode(() => {});
164
// Left([ValidationError]) - functions not valid in JSON
165
166
// Encoding is identity for valid JSON
167
const encoded = Json.encode({ key: [1, 2, 3], flag: true });
168
// { key: [1, 2, 3], flag: true }
169
```
170
171
### JSON String Parsing
172
173
Codec that parses JSON strings to Json values and stringifies Json values back to strings.
174
175
```typescript { .api }
176
/**
177
* Codec that parses JSON strings to Json values
178
* Handles JSON.parse() and JSON.stringify() operations safely
179
*/
180
const JsonFromString: t.Type<Json, string, string>;
181
```
182
183
**Usage Examples:**
184
185
```typescript
186
import { JsonFromString } from "io-ts-types";
187
188
// Valid JSON strings
189
const result1 = JsonFromString.decode('{"name": "Alice", "age": 25}');
190
// Right({ name: "Alice", age: 25 } as Json)
191
192
const result2 = JsonFromString.decode('[1, 2, 3, "hello"]');
193
// Right([1, 2, 3, "hello"] as Json)
194
195
const result3 = JsonFromString.decode('"simple string"');
196
// Right("simple string" as Json)
197
198
const result4 = JsonFromString.decode('42');
199
// Right(42 as Json)
200
201
const result5 = JsonFromString.decode('true');
202
// Right(true as Json)
203
204
const result6 = JsonFromString.decode('null');
205
// Right(null as Json)
206
207
// Invalid JSON strings
208
const result7 = JsonFromString.decode('{"invalid": json}');
209
// Left([ValidationError]) - invalid JSON syntax
210
211
const result8 = JsonFromString.decode('undefined');
212
// Left([ValidationError]) - undefined is not valid JSON
213
214
const result9 = JsonFromString.decode('');
215
// Left([ValidationError]) - empty string is not valid JSON
216
217
// Encoding Json back to string
218
const jsonValue = { name: "Bob", items: [1, 2, 3] };
219
const encoded = JsonFromString.encode(jsonValue);
220
// '{"name":"Bob","items":[1,2,3]}' - stringified JSON
221
222
// Round-trip example
223
const originalString = '{"test": true, "count": 42}';
224
const parsed = JsonFromString.decode(originalString);
225
if (parsed._tag === "Right") {
226
const backToString = JsonFromString.encode(parsed.right);
227
// backToString might be formatted differently but semantically equivalent
228
}
229
```
230
231
## Common Usage Patterns
232
233
### API Response Processing
234
235
```typescript
236
import * as t from "io-ts";
237
import { JsonFromString, Json } from "io-ts-types";
238
239
const ApiResponse = t.type({
240
status: t.string,
241
data: Json, // Any JSON data
242
metadata: Json // Any JSON metadata
243
});
244
245
// Process API response with mixed JSON data
246
const responseData = {
247
status: "success",
248
data: {
249
users: [
250
{ id: 1, name: "Alice", active: true },
251
{ id: 2, name: "Bob", active: false }
252
],
253
pagination: { page: 1, total: 100 }
254
},
255
metadata: {
256
timestamp: "2023-12-25T10:30:00Z",
257
version: "1.0",
258
cached: false
259
}
260
};
261
262
const parsed = ApiResponse.decode(responseData);
263
// Right({ status: "success", data: Json, metadata: Json })
264
265
// Safe access to JSON data
266
if (parsed._tag === "Right") {
267
const { data, metadata } = parsed.right;
268
269
// data and metadata are typed as Json, enabling safe manipulation
270
console.log(`API version: ${(metadata as any).version}`);
271
console.log(`User count: ${((data as any).users as any[]).length}`);
272
}
273
```
274
275
### Configuration File Processing
276
277
```typescript
278
import * as t from "io-ts";
279
import { JsonFromString } from "io-ts-types";
280
281
const ConfigFile = t.type({
282
name: t.string,
283
settings: JsonFromString, // JSON string that gets parsed
284
overrides: JsonFromString
285
});
286
287
// Configuration with JSON strings
288
const configData = {
289
name: "MyApp",
290
settings: '{"theme": "dark", "notifications": true, "timeout": 30000}',
291
overrides: '{"debug": true, "logLevel": "verbose"}'
292
};
293
294
const validated = ConfigFile.decode(configData);
295
296
if (validated._tag === "Right") {
297
const config = validated.right;
298
299
// settings and overrides are now parsed JSON objects
300
console.log(`Theme: ${(config.settings as any).theme}`);
301
console.log(`Debug mode: ${(config.overrides as any).debug}`);
302
303
// Can encode back to JSON strings
304
const settingsString = JsonFromString.encode(config.settings);
305
// '{"theme":"dark","notifications":true,"timeout":30000}'
306
}
307
```
308
309
### Dynamic Content Handling
310
311
```typescript
312
import * as t from "io-ts";
313
import { Json, JsonRecord, JsonArray } from "io-ts-types";
314
315
const ContentBlock = t.type({
316
type: t.string,
317
id: t.string,
318
data: Json, // Flexible JSON data
319
attributes: JsonRecord, // Must be a JSON object
320
children: JsonArray // Must be a JSON array
321
});
322
323
const contentData = {
324
type: "article",
325
id: "post_123",
326
data: {
327
title: "Getting Started",
328
body: "Welcome to our platform...",
329
published: true,
330
tags: ["tutorial", "beginner"]
331
},
332
attributes: {
333
author: "Alice",
334
category: "documentation",
335
featured: false
336
},
337
children: [
338
{ type: "paragraph", content: "First paragraph..." },
339
{ type: "image", src: "example.jpg", alt: "Example" }
340
]
341
};
342
343
const validated = ContentBlock.decode(contentData);
344
// All JSON fields are validated for proper JSON structure
345
```
346
347
### Form Data with JSON Fields
348
349
```typescript
350
import * as t from "io-ts";
351
import { JsonFromString, Json } from "io-ts-types";
352
353
const FormSubmission = t.type({
354
userId: t.string,
355
formType: t.string,
356
formData: JsonFromString, // JSON string from form
357
metadata: t.union([JsonFromString, t.null])
358
});
359
360
// Form submission with JSON fields
361
const submission = {
362
userId: "user_123",
363
formType: "contact",
364
formData: '{"name": "Alice", "email": "alice@example.com", "message": "Hello!"}',
365
metadata: '{"source": "website", "referrer": "google.com"}'
366
};
367
368
const validated = FormSubmission.decode(submission);
369
370
if (validated._tag === "Right") {
371
const form = validated.right;
372
373
// formData is now a parsed JSON object
374
const parsedData = form.formData as any;
375
console.log(`Contact from: ${parsedData.name} <${parsedData.email}>`);
376
console.log(`Message: ${parsedData.message}`);
377
378
// metadata is also parsed (or null)
379
if (form.metadata !== null) {
380
const meta = form.metadata as any;
381
console.log(`Source: ${meta.source}, Referrer: ${meta.referrer}`);
382
}
383
}
384
```
385
386
### Database Record with JSON Columns
387
388
```typescript
389
import * as t from "io-ts";
390
import { Json, JsonRecord } from "io-ts-types";
391
392
const DatabaseRecord = t.type({
393
id: t.number,
394
name: t.string,
395
data: Json, // JSON column - any valid JSON
396
settings: JsonRecord, // JSON column - must be object
397
created_at: t.string
398
});
399
400
// Database record with JSON columns
401
const record = {
402
id: 1,
403
name: "User Profile",
404
data: {
405
profile: {
406
avatar: "avatar.jpg",
407
bio: "Software developer",
408
social: {
409
twitter: "@alice_dev",
410
github: "alice-codes"
411
}
412
},
413
preferences: {
414
theme: "dark",
415
notifications: ["email", "push"],
416
privacy: { public: false }
417
}
418
},
419
settings: {
420
language: "en",
421
timezone: "UTC",
422
currency: "USD"
423
},
424
created_at: "2023-12-25T10:30:00Z"
425
};
426
427
const validated = DatabaseRecord.decode(record);
428
// Ensures data is valid JSON and settings is a valid JSON object
429
```
430
431
### API Webhook Payload
432
433
```typescript
434
import * as t from "io-ts";
435
import { Json, JsonFromString } from "io-ts-types";
436
437
const WebhookPayload = t.type({
438
event: t.string,
439
timestamp: t.string,
440
payload: Json, // Event data - any JSON structure
441
signature: t.string,
442
raw: JsonFromString // Raw payload string for signature verification
443
});
444
445
const webhookData = {
446
event: "user.created",
447
timestamp: "2023-12-25T10:30:00Z",
448
payload: {
449
user: {
450
id: 123,
451
email: "new-user@example.com",
452
created_at: "2023-12-25T10:30:00Z"
453
},
454
metadata: {
455
source: "registration_form",
456
ip_address: "192.168.1.1"
457
}
458
},
459
signature: "sha256=abc123...",
460
raw: '{"user":{"id":123,"email":"new-user@example.com","created_at":"2023-12-25T10:30:00Z"},"metadata":{"source":"registration_form","ip_address":"192.168.1.1"}}'
461
};
462
463
const validated = WebhookPayload.decode(webhookData);
464
// Validates event payload structure and parses raw JSON string
465
```