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