RFC6901 JSON Pointer implementation in OCaml using jsont
at 80754bdeb2fc5f7007eabfeed5ddd1adbf5bc4b6 686 lines 22 kB view raw
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)