···8687Each reference token becomes an `Index.t` value in the library:
88089```ocaml
90type t =
91 | Mem of string (* Object member access *)
···120121In the library, this is the `Jsont_pointer.get` function:
1220123```ocaml
124val get : t -> Jsont.json -> Jsont.json
125```
···154The empty pointer returns the whole document. In OCaml, this is
155`Jsont_pointer.root`:
1560157```ocaml
158val root : t
159(** The empty pointer that references the whole document. *)
···218**Important**: When using the OCaml library programmatically, you don't need
219to worry about escaping. The `Index.Mem` variant holds the literal key name:
2200221```ocaml
222(* To access the key "a/b", just use the literal string *)
223let pointer = Jsont_pointer.make [Mem "a/b"]
···280281The library provides both exception-raising and result-returning variants:
2820283```ocaml
284val get : t -> Jsont.json -> Jsont.json
285val get_result : t -> Jsont.json -> (Jsont.json, Jsont.Error.t) result
···365366In OCaml:
3670368```ocaml
369val add : t -> Jsont.json -> value:Jsont.json -> Jsont.json
370```
···470to think about escaping. The `Index.Mem` variant stores unescaped strings,
471and escaping happens automatically during serialization:
4720473```ocaml
474(* Create a pointer to key "a/b" - no escaping needed *)
475let p = Jsont_pointer.make [Mem "a/b"]
···484485The `Token` module exposes the escaping functions if you need them:
4860487```ocaml
488module Token : sig
489 val escape : string -> string (* "a/b" -> "a~1b" *)
···612613The library provides functions for URI fragment encoding:
6140615```ocaml
616val to_uri_fragment : t -> string
617val of_uri_fragment : string -> t
···656657## Jsont Integration
658659-The library integrates with the `Jsont` codec system for typed access:
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000660661```ocaml
662-(* Codec for JSON Pointers as JSON strings *)
663-val jsont : t Jsont.t
0000000000000000000000000000000000000664665-(* Query combinators *)
666-val path : ?absent:'a -> t -> 'a Jsont.t -> 'a Jsont.t
667-val set_path : ?allow_absent:bool -> 'a Jsont.t -> t -> 'a -> Jsont.json Jsont.t
668-val update_path : ?absent:'a -> t -> 'a Jsont.t -> Jsont.json Jsont.t
669-val delete_path : ?allow_absent:bool -> t -> Jsont.json Jsont.t
00000670```
671672-These allow you to use JSON Pointers with typed codecs rather than raw
673-`Jsont.json` values.
0000000000674675## Summary
676
···8687Each reference token becomes an `Index.t` value in the library:
8889+<!-- $MDX skip -->
90```ocaml
91type t =
92 | Mem of string (* Object member access *)
···121122In the library, this is the `Jsont_pointer.get` function:
123124+<!-- $MDX skip -->
125```ocaml
126val get : t -> Jsont.json -> Jsont.json
127```
···156The empty pointer returns the whole document. In OCaml, this is
157`Jsont_pointer.root`:
158159+<!-- $MDX skip -->
160```ocaml
161val root : t
162(** The empty pointer that references the whole document. *)
···221**Important**: When using the OCaml library programmatically, you don't need
222to worry about escaping. The `Index.Mem` variant holds the literal key name:
223224+<!-- $MDX skip -->
225```ocaml
226(* To access the key "a/b", just use the literal string *)
227let pointer = Jsont_pointer.make [Mem "a/b"]
···284285The library provides both exception-raising and result-returning variants:
286287+<!-- $MDX skip -->
288```ocaml
289val get : t -> Jsont.json -> Jsont.json
290val get_result : t -> Jsont.json -> (Jsont.json, Jsont.Error.t) result
···370371In OCaml:
372373+<!-- $MDX skip -->
374```ocaml
375val add : t -> Jsont.json -> value:Jsont.json -> Jsont.json
376```
···476to think about escaping. The `Index.Mem` variant stores unescaped strings,
477and escaping happens automatically during serialization:
478479+<!-- $MDX skip -->
480```ocaml
481(* Create a pointer to key "a/b" - no escaping needed *)
482let p = Jsont_pointer.make [Mem "a/b"]
···491492The `Token` module exposes the escaping functions if you need them:
493494+<!-- $MDX skip -->
495```ocaml
496module Token : sig
497 val escape : string -> string (* "a/b" -> "a~1b" *)
···620621The library provides functions for URI fragment encoding:
622623+<!-- $MDX skip -->
624```ocaml
625val to_uri_fragment : t -> string
626val of_uri_fragment : string -> t
···665666## Jsont Integration
667668+The library integrates with the `Jsont` codec system, allowing you to
669+combine JSON Pointer navigation with typed decoding. This is powerful
670+because you can point to a location in a JSON document and decode it
671+directly to an OCaml type.
672+673+Let's set up our OCaml environment and explore these features:
674+675+```ocaml
676+# open Jsont_pointer;;
677+# let parse_json s =
678+ match Jsont_bytesrw.decode_string Jsont.json s with
679+ | Ok json -> json
680+ | Error e -> failwith e;;
681+val parse_json : string -> Jsont.json = <fun>
682+# let json_to_string json =
683+ match Jsont_bytesrw.encode_string ~format:Jsont.Minify Jsont.json json with
684+ | Ok s -> s
685+ | Error e -> failwith e;;
686+val json_to_string : Jsont.json -> string = <fun>
687+```
688+689+### Working with JSON Values
690+691+Let's create a sample configuration document:
692+693+```ocaml
694+# let config_json = parse_json {|{
695+ "database": {
696+ "host": "localhost",
697+ "port": 5432,
698+ "credentials": {"username": "admin", "password": "secret"}
699+ },
700+ "features": ["auth", "logging", "metrics"]
701+ }|};;
702+val config_json : Jsont.json =
703+ Jsont.Object
704+ ([(("database", <abstr>),
705+ Jsont.Object
706+ ([(("host", <abstr>), Jsont.String ("localhost", <abstr>));
707+ (("port", <abstr>), Jsont.Number (5432., <abstr>));
708+ (("credentials", <abstr>),
709+ Jsont.Object
710+ ([(("username", <abstr>), Jsont.String ("admin", <abstr>));
711+ (("password", <abstr>), Jsont.String ("secret", <abstr>))],
712+ <abstr>))],
713+ <abstr>));
714+ (("features", <abstr>),
715+ Jsont.Array
716+ ([Jsont.String ("auth", <abstr>); Jsont.String ("logging", <abstr>);
717+ Jsont.String ("metrics", <abstr>)],
718+ <abstr>))],
719+ <abstr>)
720+```
721+722+### Creating and Using Pointers
723+724+Create a pointer and use it to extract values:
725+726+```ocaml
727+# let host_ptr = of_string "/database/host";;
728+val host_ptr : t = <abstr>
729+# let host_value = get host_ptr config_json;;
730+val host_value : Jsont.json = Jsont.String ("localhost", <abstr>)
731+# match host_value with
732+ | Jsont.String (s, _) -> s
733+ | _ -> failwith "expected string";;
734+- : string = "localhost"
735+```
736+737+### Building Pointers Programmatically
738+739+Instead of parsing strings, you can build pointers from indices:
740+741+```ocaml
742+# let port_ptr = make [Mem "database"; Mem "port"];;
743+val port_ptr : t = <abstr>
744+# to_string port_ptr;;
745+- : string = "/database/port"
746+# match get port_ptr config_json with
747+ | Jsont.Number (n, _) -> int_of_float n
748+ | _ -> failwith "expected number";;
749+- : int = 5432
750+```
751+752+For array access, use `Nth`:
753+754+```ocaml
755+# let first_feature_ptr = make [Mem "features"; Nth 0];;
756+val first_feature_ptr : t = <abstr>
757+# match get first_feature_ptr config_json with
758+ | Jsont.String (s, _) -> s
759+ | _ -> failwith "expected string";;
760+- : string = "auth"
761+```
762+763+### Pointer Navigation
764+765+You can build pointers incrementally using `append`:
766+767+```ocaml
768+# let db_ptr = of_string "/database";;
769+val db_ptr : t = <abstr>
770+# let creds_ptr = append db_ptr (Mem "credentials");;
771+val creds_ptr : t = <abstr>
772+# let user_ptr = append creds_ptr (Mem "username");;
773+val user_ptr : t = <abstr>
774+# to_string user_ptr;;
775+- : string = "/database/credentials/username"
776+# match get user_ptr config_json with
777+ | Jsont.String (s, _) -> s
778+ | _ -> failwith "expected string";;
779+- : string = "admin"
780+```
781+782+### Safe Access with `find`
783+784+Use `find` when you're not sure if a path exists:
785+786+```ocaml
787+# find (of_string "/database/timeout") config_json;;
788+- : Jsont.json option = None
789+# find (of_string "/database/host") config_json |> Option.is_some;;
790+- : bool = true
791+```
792+793+### Typed Access with `path`
794+795+The `path` combinator combines pointer navigation with typed decoding:
796+797+```ocaml
798+# let db_host =
799+ Jsont.Json.decode
800+ (path (of_string "/database/host") Jsont.string)
801+ config_json
802+ |> Result.get_ok;;
803+val db_host : string = "localhost"
804+# let db_port =
805+ Jsont.Json.decode
806+ (path (of_string "/database/port") Jsont.int)
807+ config_json
808+ |> Result.get_ok;;
809+val db_port : int = 5432
810+```
811+812+Extract a list of strings:
813+814+```ocaml
815+# let features =
816+ Jsont.Json.decode
817+ (path (of_string "/features") Jsont.(list string))
818+ config_json
819+ |> Result.get_ok;;
820+val features : string list = ["auth"; "logging"; "metrics"]
821+```
822+823+### Default Values with `~absent`
824+825+Use `~absent` to provide a default when a path doesn't exist:
826+827+```ocaml
828+# let timeout =
829+ Jsont.Json.decode
830+ (path ~absent:30 (of_string "/database/timeout") Jsont.int)
831+ config_json
832+ |> Result.get_ok;;
833+val timeout : int = 30
834+```
835+836+### Mutation Operations
837+838+The library provides mutation functions for modifying JSON:
839+840+```ocaml
841+# let sample = parse_json {|{"name": "Alice", "scores": [85, 92, 78]}|};;
842+val sample : Jsont.json =
843+ Jsont.Object
844+ ([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
845+ (("scores", <abstr>),
846+ Jsont.Array
847+ ([Jsont.Number (85., <abstr>); Jsont.Number (92., <abstr>);
848+ Jsont.Number (78., <abstr>)],
849+ <abstr>))],
850+ <abstr>)
851+```
852+853+**Add** a new field:
854+855+```ocaml
856+# let with_email = add (of_string "/email") sample
857+ ~value:(Jsont.Json.string "alice@example.com");;
858+val with_email : Jsont.json =
859+ Jsont.Object
860+ ([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
861+ (("scores", <abstr>),
862+ Jsont.Array
863+ ([Jsont.Number (85., <abstr>); Jsont.Number (92., <abstr>);
864+ Jsont.Number (78., <abstr>)],
865+ <abstr>));
866+ (("email", <abstr>), Jsont.String ("alice@example.com", <abstr>))],
867+ <abstr>)
868+# json_to_string with_email;;
869+- : string =
870+"{\"name\":\"Alice\",\"scores\":[85,92,78],\"email\":\"alice@example.com\"}"
871+```
872+873+**Add** to an array using `-` (append):
874+875+```ocaml
876+# let with_new_score = add (of_string "/scores/-") sample
877+ ~value:(Jsont.Json.number 95.);;
878+val with_new_score : Jsont.json =
879+ Jsont.Object
880+ ([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
881+ (("scores", <abstr>),
882+ Jsont.Array
883+ ([Jsont.Number (85., <abstr>); Jsont.Number (92., <abstr>);
884+ Jsont.Number (78., <abstr>); Jsont.Number (95., <abstr>)],
885+ <abstr>))],
886+ <abstr>)
887+# json_to_string with_new_score;;
888+- : string = "{\"name\":\"Alice\",\"scores\":[85,92,78,95]}"
889+```
890+891+**Replace** an existing value:
892+893+```ocaml
894+# let renamed = replace (of_string "/name") sample
895+ ~value:(Jsont.Json.string "Bob");;
896+val renamed : Jsont.json =
897+ Jsont.Object
898+ ([(("name", <abstr>), Jsont.String ("Bob", <abstr>));
899+ (("scores", <abstr>),
900+ Jsont.Array
901+ ([Jsont.Number (85., <abstr>); Jsont.Number (92., <abstr>);
902+ Jsont.Number (78., <abstr>)],
903+ <abstr>))],
904+ <abstr>)
905+# json_to_string renamed;;
906+- : string = "{\"name\":\"Bob\",\"scores\":[85,92,78]}"
907+```
908+909+**Remove** a value:
910+911+```ocaml
912+# let without_first = remove (of_string "/scores/0") sample;;
913+val without_first : Jsont.json =
914+ Jsont.Object
915+ ([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
916+ (("scores", <abstr>),
917+ Jsont.Array
918+ ([Jsont.Number (92., <abstr>); Jsont.Number (78., <abstr>)], <abstr>))],
919+ <abstr>)
920+# json_to_string without_first;;
921+- : string = "{\"name\":\"Alice\",\"scores\":[92,78]}"
922+```
923+924+### Nested Path Extraction
925+926+You can extract values from deeply nested structures:
927928```ocaml
929+# let org_json = parse_json {|{
930+ "organization": {
931+ "owner": {"name": "Alice", "email": "alice@example.com", "age": 35},
932+ "members": [{"name": "Bob", "email": "bob@example.com", "age": 28}]
933+ }
934+ }|};;
935+val org_json : Jsont.json =
936+ Jsont.Object
937+ ([(("organization", <abstr>),
938+ Jsont.Object
939+ ([(("owner", <abstr>),
940+ Jsont.Object
941+ ([(("name", <abstr>), Jsont.String ("Alice", <abstr>));
942+ (("email", <abstr>),
943+ Jsont.String ("alice@example.com", <abstr>));
944+ (("age", <abstr>), Jsont.Number (35., <abstr>))],
945+ <abstr>));
946+ (("members", <abstr>),
947+ Jsont.Array
948+ ([Jsont.Object
949+ ([(("name", <abstr>), Jsont.String ("Bob", <abstr>));
950+ (("email", <abstr>),
951+ Jsont.String ("bob@example.com", <abstr>));
952+ (("age", <abstr>), Jsont.Number (28., <abstr>))],
953+ <abstr>)],
954+ <abstr>))],
955+ <abstr>))],
956+ <abstr>)
957+# Jsont.Json.decode
958+ (path (of_string "/organization/owner/name") Jsont.string)
959+ org_json
960+ |> Result.get_ok;;
961+- : string = "Alice"
962+# Jsont.Json.decode
963+ (path (of_string "/organization/members/0/age") Jsont.int)
964+ org_json
965+ |> Result.get_ok;;
966+- : int = 28
967+```
968969+### Comparison: Raw vs Typed Access
970+971+**Raw access** requires pattern matching:
972+973+```ocaml
974+# let raw_port =
975+ match get (of_string "/database/port") config_json with
976+ | Jsont.Number (f, _) -> int_of_float f
977+ | _ -> failwith "expected number";;
978+val raw_port : int = 5432
979```
980981+**Typed access** is cleaner and type-safe:
982+983+```ocaml
984+# let typed_port =
985+ Jsont.Json.decode
986+ (path (of_string "/database/port") Jsont.int)
987+ config_json
988+ |> Result.get_ok;;
989+val typed_port : int = 5432
990+```
991+992+The typed approach catches mismatches at decode time with clear errors.
993994## Summary
995