+53
-147
doc/tutorial.md
+53
-147
doc/tutorial.md
···
11
11
```ocaml
12
12
# open Jsont_pointer;;
13
13
# #install_printer Jsont_pointer_top.printer;;
14
+
# #install_printer Jsont_pointer_top.json_printer;;
14
15
# let parse_json s =
15
16
match Jsont_bytesrw.decode_string Jsont.json s with
16
17
| Ok json -> json
17
18
| Error e -> failwith e;;
18
19
val parse_json : string -> Jsont.json = <fun>
19
-
# let json_to_string json =
20
-
match Jsont_bytesrw.encode_string ~format:Jsont.Minify Jsont.json json with
21
-
| Ok s -> s
22
-
| Error e -> failwith e;;
23
-
val json_to_string : Jsont.json -> string = <fun>
24
20
```
25
21
26
22
## What is JSON Pointer?
···
44
40
]
45
41
}|};;
46
42
val users_json : Jsont.json =
47
-
Jsont.Object
48
-
([(("users", <abstr>),
49
-
Jsont.Array
50
-
([Jsont.Object
51
-
([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
52
-
(("age", <abstr>), Jsont.Number (30., <abstr>))],
53
-
<abstr>);
54
-
Jsont.Object
55
-
([(("name", <abstr>), Jsont.String ("Bob", <abstr>));
56
-
(("age", <abstr>), Jsont.Number (25., <abstr>))],
57
-
<abstr>)],
58
-
<abstr>))],
59
-
<abstr>)
43
+
{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}
60
44
```
61
45
62
46
The JSON Pointer `/users/0/name` refers to the string `"Alice"`:
···
65
49
# let ptr = of_string "/users/0/name";;
66
50
val ptr : t = [`Mem "users"; `Nth 0; `Mem "name"]
67
51
# get ptr users_json;;
68
-
- : Jsont.json = Jsont.String ("Alice", <abstr>)
52
+
- : Jsont.json = "Alice"
69
53
```
70
54
71
55
In OCaml, this is represented by the `Jsont_pointer.t` type - a sequence
···
186
170
"m~n": 8
187
171
}|};;
188
172
val rfc_example : Jsont.json =
189
-
Jsont.Object
190
-
([(("foo", <abstr>),
191
-
Jsont.Array
192
-
([Jsont.String ("bar", <abstr>); Jsont.String ("baz", <abstr>)],
193
-
<abstr>));
194
-
(("", <abstr>), Jsont.Number (0., <abstr>));
195
-
(("a/b", <abstr>), Jsont.Number (1., <abstr>));
196
-
(("c%d", <abstr>), Jsont.Number (2., <abstr>));
197
-
(("e^f", <abstr>), Jsont.Number (3., <abstr>));
198
-
(("g|h", <abstr>), Jsont.Number (4., <abstr>));
199
-
(("i\\j", <abstr>), Jsont.Number (5., <abstr>));
200
-
(("k\"l", <abstr>), Jsont.Number (6., <abstr>));
201
-
((" ", <abstr>), Jsont.Number (7., <abstr>));
202
-
(("m~n", <abstr>), Jsont.Number (8., <abstr>))],
203
-
<abstr>)
173
+
{"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8}
204
174
```
205
175
206
176
This document is carefully constructed to exercise various edge cases!
···
208
178
### The Root Pointer
209
179
210
180
```ocaml
211
-
# get root rfc_example |> json_to_string;;
212
-
- : string =
213
-
"{\"foo\":[\"bar\",\"baz\"],\"\":0,\"a/b\":1,\"c%d\":2,\"e^f\":3,\"g|h\":4,\"i\\\\j\":5,\"k\\\"l\":6,\" \":7,\"m~n\":8}"
181
+
# get root rfc_example ;;
182
+
- : Jsont.json =
183
+
{"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8}
214
184
```
215
185
216
186
The empty pointer (`root`) returns the whole document.
···
218
188
### Object Member Access
219
189
220
190
```ocaml
221
-
# get (of_string "/foo") rfc_example |> json_to_string;;
222
-
- : string = "[\"bar\",\"baz\"]"
191
+
# get (of_string "/foo") rfc_example ;;
192
+
- : Jsont.json = ["bar","baz"]
223
193
```
224
194
225
195
`/foo` accesses the member named `foo`, which is an array.
···
227
197
### Array Index Access
228
198
229
199
```ocaml
230
-
# get (of_string "/foo/0") rfc_example |> json_to_string;;
231
-
- : string = "\"bar\""
232
-
# get (of_string "/foo/1") rfc_example |> json_to_string;;
233
-
- : string = "\"baz\""
200
+
# get (of_string "/foo/0") rfc_example ;;
201
+
- : Jsont.json = "bar"
202
+
# get (of_string "/foo/1") rfc_example ;;
203
+
- : Jsont.json = "baz"
234
204
```
235
205
236
206
`/foo/0` first goes to `foo`, then accesses index 0 of the array.
···
240
210
JSON allows empty strings as object keys:
241
211
242
212
```ocaml
243
-
# get (of_string "/") rfc_example |> json_to_string;;
244
-
- : string = "0"
213
+
# get (of_string "/") rfc_example ;;
214
+
- : Jsont.json = 0
245
215
```
246
216
247
217
The pointer `/` has one token: the empty string. This accesses the member
···
252
222
The RFC example includes keys with `/` and `~` characters:
253
223
254
224
```ocaml
255
-
# get (of_string "/a~1b") rfc_example |> json_to_string;;
256
-
- : string = "1"
225
+
# get (of_string "/a~1b") rfc_example ;;
226
+
- : Jsont.json = 1
257
227
```
258
228
259
229
The token `a~1b` refers to the key `a/b`. We'll explain this escaping
260
230
[below](#escaping-special-characters).
261
231
262
232
```ocaml
263
-
# get (of_string "/m~0n") rfc_example |> json_to_string;;
264
-
- : string = "8"
233
+
# get (of_string "/m~0n") rfc_example ;;
234
+
- : Jsont.json = 8
265
235
```
266
236
267
237
The token `m~0n` refers to the key `m~n`.
···
274
244
val slash_ptr : t = [`Mem "a/b"]
275
245
# to_string slash_ptr;;
276
246
- : string = "/a~1b"
277
-
# get slash_ptr rfc_example |> json_to_string;;
278
-
- : string = "1"
247
+
# get slash_ptr rfc_example ;;
248
+
- : Jsont.json = 1
279
249
```
280
250
281
251
The library escapes it when converting to string.
···
285
255
Most characters don't need escaping in JSON Pointer strings:
286
256
287
257
```ocaml
288
-
# get (of_string "/c%d") rfc_example |> json_to_string;;
289
-
- : string = "2"
290
-
# get (of_string "/e^f") rfc_example |> json_to_string;;
291
-
- : string = "3"
292
-
# get (of_string "/g|h") rfc_example |> json_to_string;;
293
-
- : string = "4"
294
-
# get (of_string "/ ") rfc_example |> json_to_string;;
295
-
- : string = "7"
258
+
# get (of_string "/c%d") rfc_example ;;
259
+
- : Jsont.json = 2
260
+
# get (of_string "/e^f") rfc_example ;;
261
+
- : Jsont.json = 3
262
+
# get (of_string "/g|h") rfc_example ;;
263
+
- : Jsont.json = 4
264
+
# get (of_string "/ ") rfc_example ;;
265
+
- : Jsont.json = 7
296
266
```
297
267
298
268
Even a space is a valid key character!
···
404
374
405
375
```ocaml
406
376
# let obj = parse_json {|{"foo":"bar"}|};;
407
-
val obj : Jsont.json =
408
-
Jsont.Object ([(("foo", <abstr>), Jsont.String ("bar", <abstr>))], <abstr>)
377
+
val obj : Jsont.json = {"foo":"bar"}
409
378
# add (of_string "/baz") obj ~value:(Jsont.Json.string "qux")
410
-
|> json_to_string;;
411
-
- : string = "{\"foo\":\"bar\",\"baz\":\"qux\"}"
379
+
;;
380
+
- : Jsont.json = {"foo":"bar","baz":"qux"}
412
381
```
413
382
414
383
For arrays, `add` inserts BEFORE the specified index:
415
384
416
385
```ocaml
417
386
# let arr_obj = parse_json {|{"foo":["a","b"]}|};;
418
-
val arr_obj : Jsont.json =
419
-
Jsont.Object
420
-
([(("foo", <abstr>),
421
-
Jsont.Array
422
-
([Jsont.String ("a", <abstr>); Jsont.String ("b", <abstr>)], <abstr>))],
423
-
<abstr>)
387
+
val arr_obj : Jsont.json = {"foo":["a","b"]}
424
388
# add (of_string "/foo/1") arr_obj ~value:(Jsont.Json.string "X")
425
-
|> json_to_string;;
426
-
- : string = "{\"foo\":[\"a\",\"X\",\"b\"]}"
389
+
;;
390
+
- : Jsont.json = {"foo":["a","X","b"]}
427
391
```
428
392
429
393
This is where the `-` marker shines - it appends to the end:
430
394
431
395
```ocaml
432
396
# add (of_string "/foo/-") arr_obj ~value:(Jsont.Json.string "c")
433
-
|> json_to_string;;
434
-
- : string = "{\"foo\":[\"a\",\"b\",\"c\"]}"
397
+
;;
398
+
- : Jsont.json = {"foo":["a","b","c"]}
435
399
```
436
400
437
401
### Remove
···
440
404
441
405
```ocaml
442
406
# let two_fields = parse_json {|{"foo":"bar","baz":"qux"}|};;
443
-
val two_fields : Jsont.json =
444
-
Jsont.Object
445
-
([(("foo", <abstr>), Jsont.String ("bar", <abstr>));
446
-
(("baz", <abstr>), Jsont.String ("qux", <abstr>))],
447
-
<abstr>)
448
-
# remove (of_string "/baz") two_fields |> json_to_string;;
449
-
- : string = "{\"foo\":\"bar\"}"
407
+
val two_fields : Jsont.json = {"foo":"bar","baz":"qux"}
408
+
# remove (of_string "/baz") two_fields ;;
409
+
- : Jsont.json = {"foo":"bar"}
450
410
```
451
411
452
412
For arrays, it removes and shifts:
453
413
454
414
```ocaml
455
415
# let three_elem = parse_json {|{"foo":["a","b","c"]}|};;
456
-
val three_elem : Jsont.json =
457
-
Jsont.Object
458
-
([(("foo", <abstr>),
459
-
Jsont.Array
460
-
([Jsont.String ("a", <abstr>); Jsont.String ("b", <abstr>);
461
-
Jsont.String ("c", <abstr>)],
462
-
<abstr>))],
463
-
<abstr>)
464
-
# remove (of_string "/foo/1") three_elem |> json_to_string;;
465
-
- : string = "{\"foo\":[\"a\",\"c\"]}"
416
+
val three_elem : Jsont.json = {"foo":["a","b","c"]}
417
+
# remove (of_string "/foo/1") three_elem ;;
418
+
- : Jsont.json = {"foo":["a","c"]}
466
419
```
467
420
468
421
### Replace
···
471
424
472
425
```ocaml
473
426
# replace (of_string "/foo") obj ~value:(Jsont.Json.string "baz")
474
-
|> json_to_string;;
475
-
- : string = "{\"foo\":\"baz\"}"
427
+
;;
428
+
- : Jsont.json = {"foo":"baz"}
476
429
```
477
430
478
431
Unlike `add`, `replace` requires the target to already exist.
···
484
437
485
438
```ocaml
486
439
# let nested = parse_json {|{"foo":{"bar":"baz"},"qux":{}}|};;
487
-
val nested : Jsont.json =
488
-
Jsont.Object
489
-
([(("foo", <abstr>),
490
-
Jsont.Object
491
-
([(("bar", <abstr>), Jsont.String ("baz", <abstr>))], <abstr>));
492
-
(("qux", <abstr>), Jsont.Object ([], <abstr>))],
493
-
<abstr>)
440
+
val nested : Jsont.json = {"foo":{"bar":"baz"},"qux":{}}
494
441
# move ~from:(of_string "/foo/bar") ~path:(of_string "/qux/thud") nested
495
-
|> json_to_string;;
496
-
- : string = "{\"foo\":{},\"qux\":{\"thud\":\"baz\"}}"
442
+
;;
443
+
- : Jsont.json = {"foo":{},"qux":{"thud":"baz"}}
497
444
```
498
445
499
446
### Copy
···
502
449
503
450
```ocaml
504
451
# let to_copy = parse_json {|{"foo":{"bar":"baz"}}|};;
505
-
val to_copy : Jsont.json =
506
-
Jsont.Object
507
-
([(("foo", <abstr>),
508
-
Jsont.Object
509
-
([(("bar", <abstr>), Jsont.String ("baz", <abstr>))], <abstr>))],
510
-
<abstr>)
452
+
val to_copy : Jsont.json = {"foo":{"bar":"baz"}}
511
453
# copy ~from:(of_string "/foo/bar") ~path:(of_string "/foo/qux") to_copy
512
-
|> json_to_string;;
513
-
- : string = "{\"foo\":{\"bar\":\"baz\",\"qux\":\"baz\"}}"
454
+
;;
455
+
- : Jsont.json = {"foo":{"bar":"baz","qux":"baz"}}
514
456
```
515
457
516
458
### Test
···
704
646
"features": ["auth", "logging", "metrics"]
705
647
}|};;
706
648
val config_json : Jsont.json =
707
-
Jsont.Object
708
-
([(("database", <abstr>),
709
-
Jsont.Object
710
-
([(("host", <abstr>), Jsont.String ("localhost", <abstr>));
711
-
(("port", <abstr>), Jsont.Number (5432., <abstr>));
712
-
(("credentials", <abstr>),
713
-
Jsont.Object
714
-
([(("username", <abstr>), Jsont.String ("admin", <abstr>));
715
-
(("password", <abstr>), Jsont.String ("secret", <abstr>))],
716
-
<abstr>))],
717
-
<abstr>));
718
-
(("features", <abstr>),
719
-
Jsont.Array
720
-
([Jsont.String ("auth", <abstr>); Jsont.String ("logging", <abstr>);
721
-
Jsont.String ("metrics", <abstr>)],
722
-
<abstr>))],
723
-
<abstr>)
649
+
{"database":{"host":"localhost","port":5432,"credentials":{"username":"admin","password":"secret"}},"features":["auth","logging","metrics"]}
724
650
```
725
651
726
652
### Typed Access with `path`
···
778
704
}
779
705
}|};;
780
706
val org_json : Jsont.json =
781
-
Jsont.Object
782
-
([(("organization", <abstr>),
783
-
Jsont.Object
784
-
([(("owner", <abstr>),
785
-
Jsont.Object
786
-
([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
787
-
(("email", <abstr>),
788
-
Jsont.String ("alice@example.com", <abstr>));
789
-
(("age", <abstr>), Jsont.Number (35., <abstr>))],
790
-
<abstr>));
791
-
(("members", <abstr>),
792
-
Jsont.Array
793
-
([Jsont.Object
794
-
([(("name", <abstr>), Jsont.String ("Bob", <abstr>));
795
-
(("email", <abstr>),
796
-
Jsont.String ("bob@example.com", <abstr>));
797
-
(("age", <abstr>), Jsont.Number (28., <abstr>))],
798
-
<abstr>)],
799
-
<abstr>))],
800
-
<abstr>))],
801
-
<abstr>)
707
+
{"organization":{"owner":{"name":"Alice","email":"alice@example.com","age":35},"members":[{"name":"Bob","email":"bob@example.com","age":28}]}}
802
708
# Jsont.Json.decode
803
709
(path (of_string "/organization/owner/name") Jsont.string)
804
710
org_json
+1
-1
src/jsont_pointer.ml
+1
-1
src/jsont_pointer.ml