RFC6901 JSON Pointer implementation in OCaml using jsont
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(* Token escaping/unescaping per RFC 6901 Section 3-4 *)
7module Token = struct
8 type t = string
9
10 let escape s =
11 let b = Buffer.create (String.length s) in
12 String.iter (function
13 | '~' -> Buffer.add_string b "~0"
14 | '/' -> Buffer.add_string b "~1"
15 | c -> Buffer.add_char b c
16 ) s;
17 Buffer.contents b
18
19 let unescape s =
20 let len = String.length s in
21 let b = Buffer.create len in
22 let rec loop i =
23 if i >= len then Buffer.contents b
24 else match s.[i] with
25 | '~' when i + 1 >= len ->
26 Jsont.Error.msgf Jsont.Meta.none
27 "Invalid JSON Pointer: incomplete escape sequence at end"
28 | '~' ->
29 (match s.[i + 1] with
30 | '0' -> Buffer.add_char b '~'; loop (i + 2)
31 | '1' -> Buffer.add_char b '/'; loop (i + 2)
32 | c ->
33 Jsont.Error.msgf Jsont.Meta.none
34 "Invalid JSON Pointer: invalid escape sequence ~%c" c)
35 | c -> Buffer.add_char b c; loop (i + 1)
36 in
37 loop 0
38
39 (* Check if a token is a valid array index per RFC 6901 ABNF:
40 array-index = %x30 / ( %x31-39 *(%x30-39) )
41 i.e., "0" or a non-zero digit followed by any digits *)
42 let is_valid_array_index s =
43 let len = String.length s in
44 let is_digit c = c >= '0' && c <= '9' in
45 if len = 0 then None
46 else if len = 1 && s.[0] = '0' then Some 0
47 else if s.[0] >= '1' && s.[0] <= '9' then
48 let rec all_digits i =
49 if i >= len then true
50 else if is_digit s.[i] then all_digits (i + 1)
51 else false
52 in
53 if all_digits 1 then int_of_string_opt s else None
54 else None
55end
56
57(* Index type - represents how a token is interpreted in context *)
58module Index = struct
59 type t =
60 | Mem of string
61 | Nth of int
62 | End
63
64 let pp ppf = function
65 | Mem s -> Format.fprintf ppf "/%s" (Token.escape s)
66 | Nth n -> Format.fprintf ppf "/%d" n
67 | End -> Format.fprintf ppf "/-"
68
69 let equal i1 i2 = match i1, i2 with
70 | Mem s1, Mem s2 -> String.equal s1 s2
71 | Nth n1, Nth n2 -> Int.equal n1 n2
72 | End, End -> true
73 | _ -> false
74
75 let compare i1 i2 = match i1, i2 with
76 | Mem s1, Mem s2 -> String.compare s1 s2
77 | Mem _, _ -> -1
78 | _, Mem _ -> 1
79 | Nth n1, Nth n2 -> Int.compare n1 n2
80 | Nth _, End -> -1
81 | End, Nth _ -> 1
82 | End, End -> 0
83
84 let of_path_index (idx : Jsont.Path.index) : t =
85 match idx with
86 | Jsont.Path.Mem (s, _meta) -> Mem s
87 | Jsont.Path.Nth (n, _meta) -> Nth n
88
89 let to_path_index (idx : t) : Jsont.Path.index option =
90 match idx with
91 | Mem s -> Some (Jsont.Path.Mem (s, Jsont.Meta.none))
92 | Nth n -> Some (Jsont.Path.Nth (n, Jsont.Meta.none))
93 | End -> None
94end
95
96(* Internal representation: raw unescaped tokens.
97 Per RFC 6901, interpretation as member name vs array index
98 depends on the JSON value type at evaluation time. *)
99module Segment = struct
100 type t =
101 | Token of string (* Unescaped reference token *)
102 | End (* The "-" token for end-of-array *)
103
104 let of_escaped_string s =
105 if s = "-" then End
106 else Token (Token.unescape s)
107
108 let to_escaped_string = function
109 | Token s -> Token.escape s
110 | End -> "-"
111
112 (* Convert to Index for a given JSON value type *)
113 let to_index seg ~for_array =
114 match seg with
115 | End -> Index.End
116 | Token s ->
117 if for_array then
118 match Token.is_valid_array_index s with
119 | Some n -> Index.Nth n
120 | None -> Index.Mem s (* Invalid index becomes member for error msg *)
121 else
122 Index.Mem s
123
124 (* Convert from Index *)
125 let of_index = function
126 | Index.End -> End
127 | Index.Mem s -> Token s
128 | Index.Nth n -> Token (string_of_int n)
129end
130
131(* Pointer type - list of segments *)
132type t = Segment.t list
133
134let root = []
135
136let is_root p = p = []
137
138(* Convert indices to segments *)
139let make indices = List.map Segment.of_index indices
140
141(* Convert segments to indices, assuming array context for numeric tokens *)
142let indices p = List.map (fun seg -> Segment.to_index seg ~for_array:true) p
143
144let append p idx = p @ [Segment.of_index idx]
145
146let concat p1 p2 = p1 @ p2
147
148let parent p = match List.rev p with
149 | [] -> None
150 | _ :: rest -> Some (List.rev rest)
151
152let last p = match List.rev p with
153 | [] -> None
154 | seg :: _ -> Some (Segment.to_index seg ~for_array:true)
155
156(* Parsing *)
157
158let of_string s =
159 if s = "" then root
160 else if s.[0] <> '/' then
161 Jsont.Error.msgf Jsont.Meta.none
162 "Invalid JSON Pointer: must be empty or start with '/': %s" s
163 else
164 let rest = String.sub s 1 (String.length s - 1) in
165 let tokens = String.split_on_char '/' rest in
166 List.map Segment.of_escaped_string tokens
167
168let of_string_result s =
169 try Ok (of_string s)
170 with Jsont.Error e -> Error (Jsont.Error.to_string e)
171
172(* URI fragment percent-decoding *)
173let hex_value c =
174 if c >= '0' && c <= '9' then Char.code c - Char.code '0'
175 else if c >= 'A' && c <= 'F' then Char.code c - Char.code 'A' + 10
176 else if c >= 'a' && c <= 'f' then Char.code c - Char.code 'a' + 10
177 else -1
178
179let percent_decode s =
180 let len = String.length s in
181 let b = Buffer.create len in
182 let rec loop i =
183 if i >= len then Buffer.contents b
184 else match s.[i] with
185 | '%' when i + 2 < len ->
186 let h1 = hex_value s.[i + 1] in
187 let h2 = hex_value s.[i + 2] in
188 if h1 >= 0 && h2 >= 0 then begin
189 Buffer.add_char b (Char.chr ((h1 lsl 4) lor h2));
190 loop (i + 3)
191 end else
192 Jsont.Error.msgf Jsont.Meta.none
193 "Invalid percent-encoding at position %d" i
194 | '%' ->
195 Jsont.Error.msgf Jsont.Meta.none
196 "Incomplete percent-encoding at position %d" i
197 | c -> Buffer.add_char b c; loop (i + 1)
198 in
199 loop 0
200
201let of_uri_fragment s =
202 of_string (percent_decode s)
203
204let of_uri_fragment_result s =
205 try Ok (of_uri_fragment s)
206 with Jsont.Error e -> Error (Jsont.Error.to_string e)
207
208(* Serialization *)
209
210let to_string p =
211 if p = [] then ""
212 else
213 let b = Buffer.create 64 in
214 List.iter (fun seg ->
215 Buffer.add_char b '/';
216 Buffer.add_string b (Segment.to_escaped_string seg)
217 ) p;
218 Buffer.contents b
219
220(* URI fragment percent-encoding *)
221let needs_percent_encoding c =
222 (* RFC 3986 fragment: unreserved / pct-encoded / sub-delims / ":" / "@" / "/" / "?" *)
223 (* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" *)
224 (* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" *)
225 not (
226 (c >= 'A' && c <= 'Z') ||
227 (c >= 'a' && c <= 'z') ||
228 (c >= '0' && c <= '9') ||
229 c = '-' || c = '.' || c = '_' || c = '~' ||
230 c = '!' || c = '$' || c = '&' || c = '\'' ||
231 c = '(' || c = ')' || c = '*' || c = '+' ||
232 c = ',' || c = ';' || c = '=' ||
233 c = ':' || c = '@' || c = '/' || c = '?'
234 )
235
236let hex_char n =
237 if n < 10 then Char.chr (Char.code '0' + n)
238 else Char.chr (Char.code 'A' + n - 10)
239
240let percent_encode s =
241 let b = Buffer.create (String.length s * 3) in
242 String.iter (fun c ->
243 if needs_percent_encoding c then begin
244 let code = Char.code c in
245 Buffer.add_char b '%';
246 Buffer.add_char b (hex_char (code lsr 4));
247 Buffer.add_char b (hex_char (code land 0xF))
248 end else
249 Buffer.add_char b c
250 ) s;
251 Buffer.contents b
252
253let to_uri_fragment p =
254 percent_encode (to_string p)
255
256let pp ppf p =
257 Format.pp_print_string ppf (to_string p)
258
259(* Comparison *)
260
261let segment_equal s1 s2 = match s1, s2 with
262 | Segment.Token t1, Segment.Token t2 -> String.equal t1 t2
263 | Segment.End, Segment.End -> true
264 | _ -> false
265
266let segment_compare s1 s2 = match s1, s2 with
267 | Segment.Token t1, Segment.Token t2 -> String.compare t1 t2
268 | Segment.Token _, Segment.End -> -1
269 | Segment.End, Segment.Token _ -> 1
270 | Segment.End, Segment.End -> 0
271
272let equal p1 p2 =
273 List.equal segment_equal p1 p2
274
275let compare p1 p2 =
276 List.compare segment_compare p1 p2
277
278(* Path conversion *)
279
280let segment_of_path_index (idx : Jsont.Path.index) : Segment.t =
281 match idx with
282 | Jsont.Path.Mem (s, _meta) -> Segment.Token s
283 | Jsont.Path.Nth (n, _meta) -> Segment.Token (string_of_int n)
284
285let of_path (p : Jsont.Path.t) : t =
286 List.rev_map segment_of_path_index (Jsont.Path.rev_indices p)
287
288let to_path p =
289 let rec convert acc = function
290 | [] -> Some acc
291 | Segment.End :: _ -> None
292 | Segment.Token s :: rest ->
293 (* For path conversion, we need to decide if it's a member or index.
294 We use array context for numeric tokens since Jsont.Path distinguishes. *)
295 let acc' = match Token.is_valid_array_index s with
296 | Some n -> Jsont.Path.nth ~meta:Jsont.Meta.none n acc
297 | None -> Jsont.Path.mem ~meta:Jsont.Meta.none s acc
298 in
299 convert acc' rest
300 in
301 convert Jsont.Path.root p
302
303let to_path_exn p =
304 match to_path p with
305 | Some path -> path
306 | None ->
307 Jsont.Error.msgf Jsont.Meta.none
308 "Cannot convert JSON Pointer with '-' index to Jsont.Path"
309
310(* Evaluation helpers *)
311
312let json_sort_string (j : Jsont.json) =
313 match j with
314 | Null _ -> "null"
315 | Bool _ -> "boolean"
316 | Number _ -> "number"
317 | String _ -> "string"
318 | Array _ -> "array"
319 | Object _ -> "object"
320
321let get_member name (obj : Jsont.object') =
322 List.find_opt (fun ((n, _), _) -> String.equal n name) obj
323
324let get_nth n (arr : Jsont.json list) =
325 if n < 0 || n >= List.length arr then None
326 else Some (List.nth arr n)
327
328(* Evaluation *)
329
330let rec eval_get p json =
331 match p with
332 | [] -> json
333 | Segment.End :: _ ->
334 Jsont.Error.msgf (Jsont.Json.meta json)
335 "JSON Pointer: '-' (end marker) refers to nonexistent array element"
336 | Segment.Token token :: rest ->
337 (match json with
338 | Jsont.Object (members, _) ->
339 (* For objects, token is always a member name *)
340 (match get_member token members with
341 | Some (_, value) -> eval_get rest value
342 | None ->
343 Jsont.Error.msgf (Jsont.Json.meta json)
344 "JSON Pointer: member '%s' not found" token)
345 | Jsont.Array (elements, _) ->
346 (* For arrays, token must be a valid array index *)
347 (match Token.is_valid_array_index token with
348 | Some n ->
349 (match get_nth n elements with
350 | Some value -> eval_get rest value
351 | None ->
352 Jsont.Error.msgf (Jsont.Json.meta json)
353 "JSON Pointer: index %d out of bounds (array has %d elements)"
354 n (List.length elements))
355 | None ->
356 Jsont.Error.msgf (Jsont.Json.meta json)
357 "JSON Pointer: invalid array index '%s'" token)
358 | _ ->
359 Jsont.Error.msgf (Jsont.Json.meta json)
360 "JSON Pointer: cannot index into %s with '%s'"
361 (json_sort_string json) token)
362
363let get p json = eval_get p json
364
365let get_result p json =
366 try Ok (get p json)
367 with Jsont.Error e -> Error e
368
369let find p json =
370 try Some (get p json)
371 with Jsont.Error _ -> None
372
373(* Mutation helpers *)
374
375let set_member name value (obj : Jsont.object') : Jsont.object' =
376 let rec loop found acc = function
377 | [] ->
378 if found then List.rev acc
379 else List.rev_append acc [((name, Jsont.Meta.none), value)]
380 | ((n, m), _) :: rest when String.equal n name ->
381 loop true (((n, m), value) :: acc) rest
382 | mem :: rest ->
383 loop found (mem :: acc) rest
384 in
385 loop false [] obj
386
387let remove_member name (obj : Jsont.object') : Jsont.object' =
388 List.filter (fun ((n, _), _) -> not (String.equal n name)) obj
389
390let insert_at n value lst =
391 let rec loop i acc = function
392 | rest when i = n -> List.rev_append acc (value :: rest)
393 | [] -> List.rev acc
394 | h :: t -> loop (i + 1) (h :: acc) t
395 in
396 loop 0 [] lst
397
398let remove_at n lst =
399 List.filteri (fun i _ -> i <> n) lst
400
401let replace_at n value lst =
402 List.mapi (fun i v -> if i = n then value else v) lst
403
404(* Common navigation for mutation operations *)
405
406let navigate_to_child token json ~on_object ~on_array ~on_other =
407 match json with
408 | Jsont.Object (members, meta) -> on_object members meta
409 | Jsont.Array (elements, meta) ->
410 (match Token.is_valid_array_index token with
411 | Some n -> on_array elements meta n
412 | None ->
413 Jsont.Error.msgf (Jsont.Json.meta json)
414 "JSON Pointer: invalid array index '%s'" token)
415 | _ -> on_other ()
416
417let error_member_not_found json token =
418 Jsont.Error.msgf (Jsont.Json.meta json) "JSON Pointer: member '%s' not found" token
419
420let error_index_out_of_bounds json n =
421 Jsont.Error.msgf (Jsont.Json.meta json) "JSON Pointer: index %d out of bounds" n
422
423let error_cannot_navigate json =
424 Jsont.Error.msgf (Jsont.Json.meta json)
425 "JSON Pointer: cannot navigate through %s" (json_sort_string json)
426
427(* Mutation: set *)
428
429let rec eval_set p value json =
430 match p with
431 | [] -> value
432 | [Segment.End] ->
433 (match json with
434 | Jsont.Array (elements, meta) -> Jsont.Array (elements @ [value], meta)
435 | _ ->
436 Jsont.Error.msgf (Jsont.Json.meta json)
437 "JSON Pointer: '-' can only be used on arrays, got %s"
438 (json_sort_string json))
439 | Segment.End :: _ ->
440 Jsont.Error.msgf (Jsont.Json.meta json)
441 "JSON Pointer: '-' (end marker) refers to nonexistent array element"
442 | [Segment.Token token] ->
443 navigate_to_child token json
444 ~on_object:(fun members meta ->
445 if Option.is_some (get_member token members) then
446 Jsont.Object (set_member token value members, meta)
447 else
448 Jsont.Error.msgf (Jsont.Json.meta json)
449 "JSON Pointer: member '%s' not found for set" token)
450 ~on_array:(fun elements meta n ->
451 if n < List.length elements then
452 Jsont.Array (replace_at n value elements, meta)
453 else
454 Jsont.Error.msgf (Jsont.Json.meta json)
455 "JSON Pointer: index %d out of bounds for set" n)
456 ~on_other:(fun () ->
457 Jsont.Error.msgf (Jsont.Json.meta json)
458 "JSON Pointer: cannot set in %s" (json_sort_string json))
459 | Segment.Token token :: rest ->
460 navigate_to_child token json
461 ~on_object:(fun members meta ->
462 match get_member token members with
463 | Some (_, child) ->
464 Jsont.Object (set_member token (eval_set rest value child) members, meta)
465 | None -> error_member_not_found json token)
466 ~on_array:(fun elements meta n ->
467 match get_nth n elements with
468 | Some child ->
469 Jsont.Array (replace_at n (eval_set rest value child) elements, meta)
470 | None -> error_index_out_of_bounds json n)
471 ~on_other:(fun () -> error_cannot_navigate json)
472
473let set p json ~value = eval_set p value json
474
475(* Mutation: add (RFC 6902 semantics) *)
476
477let rec eval_add p value json =
478 match p with
479 | [] -> value
480 | [Segment.End] ->
481 (match json with
482 | Jsont.Array (elements, meta) -> Jsont.Array (elements @ [value], meta)
483 | _ ->
484 Jsont.Error.msgf (Jsont.Json.meta json)
485 "JSON Pointer: '-' can only be used on arrays, got %s"
486 (json_sort_string json))
487 | Segment.End :: _ ->
488 Jsont.Error.msgf (Jsont.Json.meta json)
489 "JSON Pointer: '-' in non-final position"
490 | [Segment.Token token] ->
491 navigate_to_child token json
492 ~on_object:(fun members meta ->
493 Jsont.Object (set_member token value members, meta))
494 ~on_array:(fun elements meta n ->
495 let len = List.length elements in
496 if n <= len then
497 Jsont.Array (insert_at n value elements, meta)
498 else
499 Jsont.Error.msgf (Jsont.Json.meta json)
500 "JSON Pointer: index %d out of bounds for add (array has %d elements)"
501 n len)
502 ~on_other:(fun () ->
503 Jsont.Error.msgf (Jsont.Json.meta json)
504 "JSON Pointer: cannot add to %s" (json_sort_string json))
505 | Segment.Token token :: rest ->
506 navigate_to_child token json
507 ~on_object:(fun members meta ->
508 match get_member token members with
509 | Some (_, child) ->
510 Jsont.Object (set_member token (eval_add rest value child) members, meta)
511 | None -> error_member_not_found json token)
512 ~on_array:(fun elements meta n ->
513 match get_nth n elements with
514 | Some child ->
515 Jsont.Array (replace_at n (eval_add rest value child) elements, meta)
516 | None -> error_index_out_of_bounds json n)
517 ~on_other:(fun () -> error_cannot_navigate json)
518
519let add p json ~value = eval_add p value json
520
521(* Mutation: remove *)
522
523let rec eval_remove p json =
524 match p with
525 | [] ->
526 Jsont.Error.msgf Jsont.Meta.none "JSON Pointer: cannot remove root document"
527 | [Segment.End] ->
528 Jsont.Error.msgf (Jsont.Json.meta json)
529 "JSON Pointer: '-' refers to nonexistent element"
530 | Segment.End :: _ ->
531 Jsont.Error.msgf (Jsont.Json.meta json)
532 "JSON Pointer: '-' in non-final position"
533 | [Segment.Token token] ->
534 navigate_to_child token json
535 ~on_object:(fun members meta ->
536 if Option.is_some (get_member token members) then
537 Jsont.Object (remove_member token members, meta)
538 else
539 Jsont.Error.msgf (Jsont.Json.meta json)
540 "JSON Pointer: member '%s' not found for remove" token)
541 ~on_array:(fun elements meta n ->
542 if n < List.length elements then
543 Jsont.Array (remove_at n elements, meta)
544 else
545 Jsont.Error.msgf (Jsont.Json.meta json)
546 "JSON Pointer: index %d out of bounds for remove" n)
547 ~on_other:(fun () ->
548 Jsont.Error.msgf (Jsont.Json.meta json)
549 "JSON Pointer: cannot remove from %s" (json_sort_string json))
550 | Segment.Token token :: rest ->
551 navigate_to_child token json
552 ~on_object:(fun members meta ->
553 match get_member token members with
554 | Some (_, child) ->
555 Jsont.Object (set_member token (eval_remove rest child) members, meta)
556 | None -> error_member_not_found json token)
557 ~on_array:(fun elements meta n ->
558 match get_nth n elements with
559 | Some child ->
560 Jsont.Array (replace_at n (eval_remove rest child) elements, meta)
561 | None -> error_index_out_of_bounds json n)
562 ~on_other:(fun () -> error_cannot_navigate json)
563
564let remove p json = eval_remove p json
565
566(* Mutation: replace *)
567
568let replace p json ~value =
569 (* Replace requires the target to exist, unlike add *)
570 let _ = get p json in (* Will raise if not found *)
571 eval_set p value json
572
573(* Mutation: move *)
574
575let rec is_prefix_of p1 p2 =
576 match p1, p2 with
577 | [], _ -> true
578 | _, [] -> false
579 | h1 :: t1, h2 :: t2 -> segment_equal h1 h2 && is_prefix_of t1 t2
580
581let move ~from ~path json =
582 (* Check for cycle: path cannot be a proper prefix of from *)
583 if is_prefix_of path from && not (equal path from) then
584 Jsont.Error.msgf Jsont.Meta.none
585 "JSON Pointer: move would create cycle (path is prefix of from)";
586 let value = get from json in
587 let json' = remove from json in
588 add path json' ~value
589
590(* Mutation: copy *)
591
592let copy ~from ~path json =
593 let value = get from json in
594 add path json ~value
595
596(* Mutation: test *)
597
598let test p json ~expected =
599 Option.fold ~none:false ~some:(Jsont.Json.equal expected) (find p json)
600
601(* Jsont codec *)
602
603let jsont : t Jsont.t =
604 let dec _meta s = of_string s in
605 let enc p = to_string p in
606 Jsont.Base.string (Jsont.Base.map
607 ~kind:"JSON Pointer"
608 ~doc:"RFC 6901 JSON Pointer"
609 ~dec ~enc ())
610
611let jsont_uri_fragment : t Jsont.t =
612 let dec _meta s = of_uri_fragment s in
613 let enc p = to_uri_fragment p in
614 Jsont.Base.string (Jsont.Base.map
615 ~kind:"JSON Pointer (URI fragment)"
616 ~doc:"RFC 6901 JSON Pointer in URI fragment encoding"
617 ~dec ~enc ())
618
619(* Query combinators *)
620
621let path ?absent p t =
622 let dec json =
623 match find p json with
624 | Some value ->
625 (match Jsont.Json.decode' t value with
626 | Ok v -> v
627 | Error e -> raise (Jsont.Error e))
628 | None ->
629 match absent with
630 | Some v -> v
631 | None ->
632 Jsont.Error.msgf Jsont.Meta.none
633 "JSON Pointer %s: path not found" (to_string p)
634 in
635 Jsont.map Jsont.json ~dec ~enc:(fun _ ->
636 Jsont.Error.msgf Jsont.Meta.none "path: encode not supported")
637
638let set_path ?(allow_absent = false) t p v =
639 let encoded = match Jsont.Json.encode' t v with
640 | Ok json -> json
641 | Error e -> raise (Jsont.Error e)
642 in
643 let dec json =
644 if allow_absent then
645 add p json ~value:encoded
646 else
647 set p json ~value:encoded
648 in
649 Jsont.map Jsont.json ~dec ~enc:(fun j -> j)
650
651let update_path ?absent p t =
652 let dec json =
653 let value = match find p json with
654 | Some v -> v
655 | None ->
656 match absent with
657 | Some v ->
658 (match Jsont.Json.encode' t v with
659 | Ok j -> j
660 | Error e -> raise (Jsont.Error e))
661 | None ->
662 Jsont.Error.msgf Jsont.Meta.none
663 "JSON Pointer %s: path not found" (to_string p)
664 in
665 let decoded = match Jsont.Json.decode' t value with
666 | Ok v -> v
667 | Error e -> raise (Jsont.Error e)
668 in
669 let re_encoded = match Jsont.Json.encode' t decoded with
670 | Ok j -> j
671 | Error e -> raise (Jsont.Error e)
672 in
673 set p json ~value:re_encoded
674 in
675 Jsont.map Jsont.json ~dec ~enc:(fun j -> j)
676
677let delete_path ?(allow_absent = false) p =
678 let dec json =
679 if allow_absent then
680 match find p json with
681 | Some _ -> remove p json
682 | None -> json
683 else
684 remove p json
685 in
686 Jsont.map Jsont.json ~dec ~enc:(fun j -> j)