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