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)