This repository has been archived by the owner on Dec 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathJson_decode.mli
495 lines (396 loc) · 14.5 KB
/
Json_decode.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
(** Provides a set of low level combinator primitives to decode Js.Json.t data
structures
A decoder combinator will return the decoded value if successful, or raise a
[DecodeError of string] if unsuccessful, where the string argument contains the
error message.
Decoders are designed to be combined to produce more complex decoders that can
decode arbitrary data structures, though the emphasis for this library is for
it to be {i possible} to decode any given data structure, not necessarily for
it to be {i convenient}. For convenience you should look towards opinionated
third-party libraries.
*)
type 'a decoder = Js.Json.t -> 'a
(** The type of a decoder combinator *)
exception DecodeError of string
val id : Js.Json.t decoder
(** Identity decoder.
{b Returns} the input JSON value.
Always succeeds. You would use this if you wanted to partially decode
some JSON in stages; in the first stage you could decode some portion
of the input, while using [id] to keep the rest as JSON and decoding
that in subsequent stages.
@example {[
open Json
(* returns [(1 : int, {"a": true} : Js.Json.t)] *)
let json = parseOrRaise {|{"id": 1, {"a": true}}|}
let _ = Decode.(int json, id json)
]} *)
val bool : bool decoder
(** Decodes a JSON value into a [bool]
{b Returns} a [bool] if the JSON value is a [true] or [false].
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns true *)
let _ = Json.parseOrRaise "true" |> Decode.bool
(* returns false *)
let _ = Json.parseOrRaise "false" |> Decode.bool
(* raises DecodeError *)
let _ = Json.parseOrRaise "123" |> Decode.bool
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.bool
]}
*)
val float : float decoder
(** Decodes a JSON value into a [float]
{b Returns} a [float] if the JSON value is a number.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns 1.23 *)
let _ = Json.parseOrRaise "1.23" |> Decode.float
(* returns 23. *)
let _ = Json.parseOrRaise "23" |> Decode.float
(* raises DecodeError *)
let _ = Json.parseOrRaise "true" |> Decode.float
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.float
]}
*)
val int : int decoder
(** Decodes a JSON value into an [int]
{b Returns} an [int] if the JSON value is a number.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise "23" |> Decode.int
(* raises DecodeError *)
let _ = Json.parseOrRaise "1.23" |> Decode.int
(* raises DecodeError *)
let _ = Json.parseOrRaise "true" |> Decode.int
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.int
]}
*)
val string : string decoder
(** Decodes a JSON value into a [string]
{b Returns} a [string] if the JSON value is a string.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns "foo" *)
let _ = Json.parseOrRaise "\"foo\"" |> Decode.string
(* raises DecodeError *)
let _ = Json.parseOrRaise "1.23" |> Decode.string
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.string
]}
*)
val char : char decoder
(** Decodes a JSON value into a [char]
{b Returns} a [char] if the JSON value is a single-character string.
@raise [DecodeError] if unsuccessful.
@example {[
open Json
(* returns 'a' *)
let _ = Json.parseOrRaise "\"a\"" |> Decode.char
(* raises DecodeError *)
let _ = Json.parseOrRaise "\"abc\"" |> Decode.char
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.char
]}
*)
val date : Js.Date.t decoder
(** Decodes an ISO8601-formatted JSON string into a [Js.Date.t]
{b Returns} a [Js.Date.t] if the JSON value is an IS8601-formatted string.
@raise [DecodeError] if unsuccessful
*)
val nullable : 'a decoder -> 'a Js.null decoder
(** Decodes a JSON value into an ['a Js.null]
{b Returns} [Js.null] if the JSON value is [null], or an ['a Js.null] if the
given decoder succeeds,
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns (Js.Null.return 23) *)
let _ = Json.parseOrRaise "23" |> Decode.(nullable int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "1.23" |> Decode.(nullable int)
(* returns Js.null *)
let _ = Json.parseOrRaise "null" |> Decode.(nullable int)
]}
*)
val nullAs : 'a -> 'a decoder
(** Returns the given value if the JSON value is [null]
{b Returns} an ['a] if the JSON value is [null].
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* raises DecodeError *)
let _ = Json.parseOrRaise "\"x\"" |> Decode.nullAs "x"
(* returns "x" *)
let _ = Json.parseOrRaise "null" |> Decode.nullAs "x"
(* returns None *)
let _ = Json.parseOrRaise "null" |> Decode.nullAs None
]}
*)
val array : 'a decoder -> 'a array decoder
(** Decodes a JSON array into an ['a array] using the given decoder on each element
{b Returns} an ['a array] if the JSON value is a JSON array and all its
elements are successfully decoded.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns [| 1; 2; 3 |] *)
let _ = Json.parseOrRaise "[1, 2, 3]" |> Decode.(array int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2, "c"]" |> Decode.(array int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "123" |> Decode.(array int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(array int)
]}
*)
val list : 'a decoder -> 'a list decoder
(** Decodes a JSON array into an ['a list] using the given decoder on each element
{b Returns} an ['a list] if the JSON value is a JSON array and all its
elements are successfully decoded.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns [1; 2; 3] *)
let _ = Json.parseOrRaise "[1, 2, 3]" |> Decode.(list int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2, "c"]" |> Decode.(list int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "123" |> Decode.(list int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(list int)
]}
*)
val pair : 'a decoder -> 'b decoder -> ('a * 'b) decoder
(** Decodes a JSON array with two elements into an ['a * 'b] tuple using
each of the given decoders in order.
{b Returns} an ['a * 'b] if the JSON value is a JSON array of length 2 and both
its elements are successfully decoded.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns (1, "bar") *)
let _ = Json.parseOrRaise "[1, \"bar\"]" |> Decode.(pair int string)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2]" |> Decode.(pair int string)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2, 3]" |> Decode.(pair int int)
]}
*)
val tuple2 : 'a decoder -> 'b decoder -> ('a * 'b) decoder
(** Decodes a JSON array with two elements into an ['a * 'b] tuple using
each of the given decoders in order.
{b Alias of [pair]}
{b Returns} an ['a * 'b] if the JSON value is a JSON array of length 2 and both
its elements are successfully decoded.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns (1, "bar") *)
let _ = Json.parseOrRaise "[1, \"bar\"]" |> Decode.(tuple2 int string)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2]" |> Decode.(tuple2 int string)
(* raises DecodeError *)
let _ = Json.parseOrRaise "[1, 2, 3]" |> Decode.(tuple2 int int)
]}
*)
val tuple3 : 'a decoder -> 'b decoder -> 'c decoder -> ('a * 'b * 'c) decoder
(** Decodes a JSON array with three elements into an ['a * 'b * 'c] tuple using
each of the given decoders in order.
{b Returns} an ['a * 'b * 'c] if the JSON value is a JSON array of length 3 and
all its elements are successfully decoded.
@raise [DecodeError] if unsuccessful
*)
val tuple4 : 'a decoder -> 'b decoder -> 'c decoder -> 'd decoder -> ('a * 'b * 'c * 'd) decoder
(** Decodes a JSON array with four elements into an ['a * 'b * 'c * 'd] tuple
using each of the given decoders in order.
{b Returns} an ['a * 'b * 'c * 'd] if the JSON value is a JSON array of length 4
and all its elements are successfully decoded.
@raise [DecodeError] if unsuccessful
*)
val dict : 'a decoder -> 'a Js.Dict.t decoder
(** Decodes a JSON object into a dict using the given decoder on each of its values
{b Returns} an ['a Js.Dict.t] if the JSON value is a JSON object and all its
values are successfully decoded.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns (Js.Dict.fromList [("x", 23); ("y", 42)]) *)
let _ = Json.parseOrRaise {| { "x": 23, "y": 42 } |} |> Decode.(dict int)
(* raises DecodeError *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(dict int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "123" |> Decode.(dict int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(dict int)
]}
*)
val field : string -> 'a decoder -> 'a decoder
(** Decodes a JSON object with a specific field into the value of that field
{b Returns} an ['a] if the JSON value is a JSON object with the given field
and a value that is successfully decoded with the given decoder.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise {| { "x": 23, "y": 42 } |} |> Decode.(field "x" int)
(* returns 23 *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(field "x" int)
(* raises DecodeError *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(field "y" int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "123" |> Decode.(field "x" int)
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(field "x" int)
]}
*)
val at : string list -> 'a decoder -> 'a decoder
(** Same as [field] but takes a top level field and a list of nested fields for decoding nested values.
{b Returns} an ['a] if the JSON value is a JSON object with the given field
and a value that is successfully decoded with the given decoder.
@raise [DecodeError] if unsuccessful
@raise [Invalid_argument] if list of fields is empty
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise {| { "x": {"foo": 23}, "y": 42 } |} |> Decode.(at ["x"; "foo"] int)
(* raises DecodeError *)
let _ = Json.parseOrRaise {| { "x": null, "y": "b" } |} |> Decode.(at ["x"; "foo"] int)
]}
*)
val optional : 'a decoder -> 'a option decoder
(** Maps a decoder [result] to an option
{b Returns} [Some of 'a] if the given decoder is successful, [None] if
it is not.
This decoder will never raise a [DecodeError]. Its purpose is to catch and
transform [DecodeError]'s of a given decoder into [None]s by mapping its
[result] into an [option]. This prevents a decoder error from terminating
a composite decoder, and is useful to decode optional JSON object fields.
@example {[
open Json
(* returns (Some 23) *)
let _ = Json.parseOrRaise "23" |> Decode.(optional int)
(* returns None *)
let _ = Json.parseOrRaise 1.23 |> Decode.(optional int)
(* returns None *)
let _ = Json.parseOrRaise "null" |> Decode.(optional int)
(* returns (Some 23) *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(optional (field "x" int))
(* returns None *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(optional (field "y" int))
(* returns None *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(optional (field "z" int))
(* returns (Some 23) *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(field "x" (optional int))
(* returns None *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(field "y" (optional int))
(* raises DecodeError *)
let _ = Json.parseOrRaise {| { "x": 23, "y": "b" } |} |> Decode.(field "z" (optional int))
]}
*)
val oneOf : 'a decoder list -> 'a decoder
(** Tries each [decoder] in order, retunring the result of the first that succeeds
{b Returns} an ['a] if one of the decoders succeed.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise "23" |> Decode.(oneOf [int; field "x" int])
(* returns 42 *)
let _ = Json.parseOrRaise {| { "x": 42 } |} |> Decode.(oneOf [int; field "x" int])
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(oneOf [int; field "x" int]
]}
*)
val either : 'a decoder -> 'a decoder -> 'a decoder
(** Tries each [decoder] in order, retunring the result of the first that succeeds
{b Returns} an ['a] if one of the decoders succeed.
@raise [DecodeError] if unsuccessful
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise "23" |> Decode.(either int (field "x" int))
(* returns 42 *)
let _ = Json.parseOrRaise {| { "x": 42 } |} |> Decode.(either int (field "x" int))
(* raises DecodeError *)
let _ = Json.parseOrRaise "null" |> Decode.(either int (field "x" int))
]}
*)
val withDefault : 'a -> 'a decoder -> 'a decoder
(** Tries the given [decoder] and returns its result if it succeeds, or the
given default value if it fails.
{b Returns} an ['a].
@example {[
open Json
(* returns 23 *)
let _ = Json.parseOrRaise "23" |> Decode.withDefault 0 int
(* returns 0 *)
let _ = Json.parseOrRaise "\"x\"" |> Decode.withDefault 0 int
(* returns 0 *)
let _ = Json.parseOrRaise "null" |> Decode.withDefault 0 int
]}
*)
val map : ('a -> 'b) -> 'a decoder -> 'b decoder
(** Returns a decoder that maps the result of the given decoder if successful
{b Returns} a ['b] if the given decoder succeeds.
@example {[
open Json
(* returns 46 *)
let _ = Json.parseOrRaise "23" |> Decode.map (fun x -> x + x) int
]}
*)
val andThen : ('a -> 'b decoder) -> 'a decoder -> 'b decoder
(** Returns a decoder that maps the result of the given decoder if successful
{b Returns} an ['a] if both decoders succeed.
@example {[
(* Decode a JSON tree structure *)
type 'a tree =
| Node of 'a * 'a tree list
| Leaf of 'a
module Decode = struct
open Json.Decode
let rec tree decoder =
field "type" string |> andThen (
function | "node" -> node decoder
| "leaf" -> leaf decoder
| _ -> failwith "unknown node type"
)
and node decoder json =
Node (
(json |> field "value" decoder),
(json |> field "children" (array (tree decoder) |> map Array.to_list))
)
and leaf decoder json =
Leaf (json |> field "value" decoder)
end
let json = {| {
"type": "node",
"value": 9,
"children": [{
"type": "node",
"value": 5,
"children": [{
"type": "leaf",
"value": 3
}, {
"type": "leaf",
"value": 2
}]
}, {
"type": "leaf",
"value": 4
}]
} |}
let myTree =
json |> Json.parseOrRaise
|> Decode.tree Json.Decode.int
]}
*)