···2424 }
2525 .x-ocaml-wrapper { max-width: 600px; }
2626 }
2727-</style></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> – <a href="index.html">Index</a> » tutorial</nav><header class="odoc-preamble"><h1 id="json-pointer-tutorial"><a href="#json-pointer-tutorial" class="anchor"></a>JSON Pointer Tutorial</h1><p>This tutorial introduces JSON Pointer as defined in <a href="https://www.rfc-editor.org/rfc/rfc6901">RFC 6901</a>, and demonstrates the <code>json-pointer</code> OCaml library through interactive examples.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#json-pointer-vs-json-path">JSON Pointer vs JSON Path</a></li><li><a href="#setup">Setup</a></li><li><a href="#what-is-json-pointer?">What is JSON Pointer?</a></li><li><a href="#syntax:-reference-tokens">Syntax: Reference Tokens</a><ul><li><a href="#the-index-type">The Index Type</a></li><li><a href="#invalid-syntax">Invalid Syntax</a></li></ul></li><li><a href="#evaluation:-navigating-json">Evaluation: Navigating JSON</a><ul><li><a href="#the-root-pointer">The Root Pointer</a></li><li><a href="#object-member-access">Object Member Access</a></li><li><a href="#array-index-access">Array Index Access</a></li><li><a href="#empty-string-as-key">Empty String as Key</a></li><li><a href="#keys-with-special-characters">Keys with Special Characters</a></li><li><a href="#other-special-characters-(no-escaping-needed)">Other Special Characters (No Escaping Needed)</a></li><li><a href="#error-conditions">Error Conditions</a></li><li><a href="#array-index-rules">Array Index Rules</a></li></ul></li><li><a href="#the-end-of-array-marker:---and-type-safety">The End-of-Array Marker: <code>-</code> and Type Safety</a><ul><li><a href="#navigation-vs-append-pointers">Navigation vs Append Pointers</a></li><li><a href="#why-two-pointer-types?">Why Two Pointer Types?</a></li><li><a href="#creating-append-pointers-programmatically">Creating Append Pointers Programmatically</a></li></ul></li><li><a href="#mutation-operations">Mutation Operations</a><ul><li><a href="#add">Add</a></li><li><a href="#ergonomic-mutation-with-any">Ergonomic Mutation with <code>any</code></a></li><li><a href="#remove">Remove</a></li><li><a href="#replace">Replace</a></li><li><a href="#move">Move</a></li><li><a href="#copy">Copy</a></li><li><a href="#test">Test</a></li></ul></li><li><a href="#escaping">Escaping Special Characters</a><ul><li><a href="#the-library-handles-escaping-automatically">The Library Handles Escaping Automatically</a></li><li><a href="#escaping-in-action">Escaping in Action</a></li><li><a href="#unescaping">Unescaping</a></li><li><a href="#the-order-matters!">The Order Matters!</a></li></ul></li><li><a href="#uri-fragment-encoding">URI Fragment Encoding</a></li><li><a href="#building-pointers-programmatically">Building Pointers Programmatically</a><ul><li><a href="#pointer-navigation">Pointer Navigation</a></li></ul></li><li><a href="#jsont-integration">Jsont Integration</a><ul><li><a href="#typed-access-with-path">Typed Access with <code>path</code></a></li><li><a href="#default-values-with-~absent">Default Values with <code>~absent</code></a></li><li><a href="#nested-path-extraction">Nested Path Extraction</a></li><li><a href="#comparison:-raw-vs-typed-access">Comparison: Raw vs Typed Access</a></li><li><a href="#updates-with-polymorphic-pointers">Updates with Polymorphic Pointers</a></li></ul></li><li><a href="#summary">Summary</a><ul><li><a href="#key-points-on-json-pointer-vs-json-path">Key Points on JSON Pointer vs JSON Path</a></li></ul></li></ul></nav></div><div class="odoc-content"><h2 id="json-pointer-vs-json-path"><a href="#json-pointer-vs-json-path" class="anchor"></a>JSON Pointer vs JSON Path</h2><p>Before diving in, it's worth understanding the difference between JSON Pointer and JSON Path, as they serve different purposes:</p><p><b>JSON Pointer</b> (RFC 6901) is an <em>indicator syntax</em> that specifies a <em>single location</em> within JSON data. It always identifies at most one value.</p><p><b>JSON Path</b> is a <em>query syntax</em> that can <em>search</em> JSON data and return <em>multiple</em> values matching specified criteria.</p><p>Use JSON Pointer when you need to address a single, specific location (like JSON Schema's <code>$ref</code>). Use JSON Path when you might need multiple results (like Kubernetes queries).</p><p>The <code>json-pointer</code> library implements JSON Pointer and integrates with the <code>Jsont.Path</code> type for representing navigation indices.</p><h2 id="setup"><a href="#setup" class="anchor"></a>Setup</h2><p>First, let's set up our environment. In the toplevel, you can load the library with <code>#require "json-pointer.top";;</code> which will automatically install pretty printers.</p><div class="x-ocaml-wrapper"><x-ocaml>Json_pointer_top.install ();;
2727+</style></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> – <a href="index.html">Index</a> » tutorial</nav><header class="odoc-preamble"><h1 id="json-pointer-tutorial"><a href="#json-pointer-tutorial" class="anchor"></a>JSON Pointer Tutorial</h1><p>This tutorial introduces JSON Pointer as defined in <a href="https://www.rfc-editor.org/rfc/rfc6901">RFC 6901</a>, and demonstrates the <code>json-pointer</code> OCaml library through interactive examples.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#json-pointer-vs-json-path">JSON Pointer vs JSON Path</a></li><li><a href="#setup">Setup</a></li><li><a href="#what-is-json-pointer?">What is JSON Pointer?</a></li><li><a href="#syntax:-reference-tokens">Syntax: Reference Tokens</a><ul><li><a href="#the-index-type">The Index Type</a></li><li><a href="#invalid-syntax">Invalid Syntax</a></li></ul></li><li><a href="#evaluation:-navigating-json">Evaluation: Navigating JSON</a><ul><li><a href="#the-root-pointer">The Root Pointer</a></li><li><a href="#object-member-access">Object Member Access</a></li><li><a href="#array-index-access">Array Index Access</a></li><li><a href="#empty-string-as-key">Empty String as Key</a></li><li><a href="#keys-with-special-characters">Keys with Special Characters</a></li><li><a href="#other-special-characters-(no-escaping-needed)">Other Special Characters (No Escaping Needed)</a></li><li><a href="#error-conditions">Error Conditions</a></li><li><a href="#array-index-rules">Array Index Rules</a></li></ul></li><li><a href="#the-end-of-array-marker:---and-type-safety">The End-of-Array Marker: <code>-</code> and Type Safety</a><ul><li><a href="#navigation-vs-append-pointers">Navigation vs Append Pointers</a></li><li><a href="#why-two-pointer-types?">Why Two Pointer Types?</a></li><li><a href="#creating-append-pointers-programmatically">Creating Append Pointers Programmatically</a></li></ul></li><li><a href="#mutation-operations">Mutation Operations</a><ul><li><a href="#add">Add</a></li><li><a href="#ergonomic-mutation-with-any">Ergonomic Mutation with <code>any</code></a></li><li><a href="#remove">Remove</a></li><li><a href="#replace">Replace</a></li><li><a href="#move">Move</a></li><li><a href="#copy">Copy</a></li><li><a href="#test">Test</a></li></ul></li><li><a href="#escaping">Escaping Special Characters</a><ul><li><a href="#the-library-handles-escaping-automatically">The Library Handles Escaping Automatically</a></li><li><a href="#escaping-in-action">Escaping in Action</a></li><li><a href="#unescaping">Unescaping</a></li><li><a href="#the-order-matters!">The Order Matters!</a></li></ul></li><li><a href="#uri-fragment-encoding">URI Fragment Encoding</a></li><li><a href="#building-pointers-programmatically">Building Pointers Programmatically</a><ul><li><a href="#pointer-navigation">Pointer Navigation</a></li></ul></li><li><a href="#jsont-integration">Jsont Integration</a><ul><li><a href="#typed-access-with-path">Typed Access with <code>path</code></a></li><li><a href="#default-values-with-~absent">Default Values with <code>~absent</code></a></li><li><a href="#nested-path-extraction">Nested Path Extraction</a></li><li><a href="#comparison:-raw-vs-typed-access">Comparison: Raw vs Typed Access</a></li><li><a href="#updates-with-polymorphic-pointers">Updates with Polymorphic Pointers</a></li></ul></li><li><a href="#summary">Summary</a><ul><li><a href="#key-points-on-json-pointer-vs-json-path">Key Points on JSON Pointer vs JSON Path</a></li></ul></li></ul></nav></div><div class="odoc-content"><h2 id="json-pointer-vs-json-path"><a href="#json-pointer-vs-json-path" class="anchor"></a>JSON Pointer vs JSON Path</h2><p>Before diving in, it's worth understanding the difference between JSON Pointer and JSON Path, as they serve different purposes:</p><p><b>JSON Pointer</b> (<a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a>) is an <em>indicator syntax</em> that specifies a <em>single location</em> within JSON data. It always identifies at most one value.</p><p><b>JSON Path</b> is a <em>query syntax</em> that can <em>search</em> JSON data and return <em>multiple</em> values matching specified criteria.</p><p>Use JSON Pointer when you need to address a single, specific location (like JSON Schema's <code>$ref</code>). Use JSON Path when you might need multiple results (like Kubernetes queries).</p><p>The <code>json-pointer</code> library implements JSON Pointer and integrates with the <code>Jsont.Path</code> type for representing navigation indices.</p><h2 id="setup"><a href="#setup" class="anchor"></a>Setup</h2><p>First, let's set up our environment. In the toplevel, you can load the library with <code>#require "json-pointer.top";;</code> which will automatically install pretty printers.</p><div class="x-ocaml-wrapper"><x-ocaml>Json_pointer_top.install ();;
2828open Json_pointer;;
2929let parse_json s =
3030 match Jsont_bytesrw.decode_string Jsont.json s with
3131 | Ok json -> json
3232- | Error e -> failwith e;;</x-ocaml></div><h2 id="what-is-json-pointer?"><a href="#what-is-json-pointer?" class="anchor"></a>What is JSON Pointer?</h2><p>From RFC 6901, Section 1:</p><p><i>JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.</i></p><p>In other words, JSON Pointer is an addressing scheme for locating values inside a JSON structure. Think of it like a filesystem path, but for JSON documents instead of files.</p><p>For example, given this JSON document:</p><div class="x-ocaml-wrapper"><x-ocaml>let users_json = parse_json {|{
3232+ | Error e -> failwith e;;</x-ocaml></div><h2 id="what-is-json-pointer?"><a href="#what-is-json-pointer?" class="anchor"></a>What is JSON Pointer?</h2><p>From <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-1">RFC 6901, Section 1</a>:</p><p><i>JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.</i></p><p>In other words, JSON Pointer is an addressing scheme for locating values inside a JSON structure. Think of it like a filesystem path, but for JSON documents instead of files.</p><p>For example, given this JSON document:</p><div class="x-ocaml-wrapper"><x-ocaml>let users_json = parse_json {|{
3333 "users": [
3434 {"name": "Alice", "age": 30},
3535 {"name": "Bob", "age": 25}
3636 ]
3737 }|};;</x-ocaml></div><p>The JSON Pointer <code>/users/0/name</code> refers to the string <code>"Alice"</code>:</p><div class="x-ocaml-wrapper"><x-ocaml>let ptr = of_string_nav "/users/0/name";;
3838-get ptr users_json;;</x-ocaml></div><p>In OCaml, this is represented by the <code>'a Json_pointer.t</code> type - a sequence of navigation steps from the document root to a target value. The phantom type parameter <code>'a</code> encodes whether this is a navigation pointer or an append pointer (more on this later).</p><h2 id="syntax:-reference-tokens"><a href="#syntax:-reference-tokens" class="anchor"></a>Syntax: Reference Tokens</h2><p>RFC 6901, Section 3 defines the syntax:</p><p><i>A JSON Pointer is a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a '/' (%x2F) character.</i></p><p>The grammar is elegantly simple:</p><pre>json-pointer = *( "/" reference-token )
3838+get ptr users_json;;</x-ocaml></div><p>In OCaml, this is represented by the <code>'a Json_pointer.t</code> type - a sequence of navigation steps from the document root to a target value. The phantom type parameter <code>'a</code> encodes whether this is a navigation pointer or an append pointer (more on this later).</p><h2 id="syntax:-reference-tokens"><a href="#syntax:-reference-tokens" class="anchor"></a>Syntax: Reference Tokens</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-3">RFC 6901, Section 3</a> defines the syntax:</p><p><i>A JSON Pointer is a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a '/' (%x2F) character.</i></p><p>The grammar is elegantly simple:</p><pre>json-pointer = *( "/" reference-token )
3939reference-token = *( unescaped / escaped )</pre><p>This means:</p><ul><li>The empty string <code>""</code> is a valid pointer (it refers to the whole document)</li><li>Every non-empty pointer starts with <code>/</code></li><li>Everything between <code>/</code> characters is a "reference token"</li></ul><p>Let's see this in action:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "";;</x-ocaml></div><p>The empty pointer has no reference tokens - it points to the root.</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo";;</x-ocaml></div><p>The pointer <code>/foo</code> has one token: <code>foo</code>. Since it's not a number, it's interpreted as an object member name (<code>Mem</code>).</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/0";;</x-ocaml></div><p>Here we have two tokens: <code>foo</code> (a member name) and <code>0</code> (interpreted as an array index <code>Nth</code>).</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/bar/baz";;</x-ocaml></div><p>Multiple tokens navigate deeper into nested structures.</p><h3 id="the-index-type"><a href="#the-index-type" class="anchor"></a>The Index Type</h3><p>Each reference token is represented using <code>Jsont.Path.index</code>:</p><pre>type index = Jsont.Path.index
4040(* = Jsont.Path.Mem of string * Jsont.Meta.t
4141 | Jsont.Path.Nth of int * Jsont.Meta.t *)</pre><p>The <code>Mem</code> constructor is for object member access, and <code>Nth</code> is for array index access. The member name is <b>unescaped</b> - you work with the actual key string (like <code>"a/b"</code>) and the library handles any escaping needed for the JSON Pointer string representation.</p><h3 id="invalid-syntax"><a href="#invalid-syntax" class="anchor"></a>Invalid Syntax</h3><p>What happens if a pointer doesn't start with <code>/</code>?</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "foo";;</x-ocaml></div><p>The RFC is strict: non-empty pointers MUST start with <code>/</code>.</p><p>For safer parsing, use <code>of_string_result</code>:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_result "foo";;
4242-of_string_result "/valid";;</x-ocaml></div><h2 id="evaluation:-navigating-json"><a href="#evaluation:-navigating-json" class="anchor"></a>Evaluation: Navigating JSON</h2><p>Now we come to the heart of JSON Pointer: evaluation. RFC 6901, Section 4 describes how a pointer is resolved against a JSON document:</p><p><i>Evaluation of a JSON Pointer begins with a reference to the root value of a JSON document and completes with a reference to some value within the document. Each reference token in the JSON Pointer is evaluated sequentially.</i></p><p>Let's use the example JSON document from RFC 6901, Section 5:</p><div class="x-ocaml-wrapper"><x-ocaml>let rfc_example = parse_json {|{
4242+of_string_result "/valid";;</x-ocaml></div><h2 id="evaluation:-navigating-json"><a href="#evaluation:-navigating-json" class="anchor"></a>Evaluation: Navigating JSON</h2><p>Now we come to the heart of JSON Pointer: evaluation. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> describes how a pointer is resolved against a JSON document:</p><p><i>Evaluation of a JSON Pointer begins with a reference to the root value of a JSON document and completes with a reference to some value within the document. Each reference token in the JSON Pointer is evaluated sequentially.</i></p><p>Let's use the example JSON document from <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-5">RFC 6901, Section 5</a>:</p><div class="x-ocaml-wrapper"><x-ocaml>let rfc_example = parse_json {|{
4343 "foo": ["bar", "baz"],
4444 "": 0,
4545 "a/b": 1,
···5959get (of_string_nav "/ ") rfc_example ;;</x-ocaml></div><p>Even a space is a valid key character!</p><h3 id="error-conditions"><a href="#error-conditions" class="anchor"></a>Error Conditions</h3><p>What happens when we try to access something that doesn't exist?</p><div class="x-ocaml-wrapper"><x-ocaml>get_result (of_string_nav "/nonexistent") rfc_example;;
6060find (of_string_nav "/nonexistent") rfc_example;;</x-ocaml></div><p>Or an out-of-bounds array index:</p><div class="x-ocaml-wrapper"><x-ocaml>find (of_string_nav "/foo/99") rfc_example;;</x-ocaml></div><p>Or try to index into a non-container:</p><div class="x-ocaml-wrapper"><x-ocaml>find (of_string_nav "/foo/0/invalid") rfc_example;;</x-ocaml></div><p>The library provides both exception-raising and result-returning variants:</p><pre>val get : nav t -> Jsont.json -> Jsont.json
6161val get_result : nav t -> Jsont.json -> (Jsont.json, Jsont.Error.t) result
6262-val find : nav t -> Jsont.json -> Jsont.json option</pre><h3 id="array-index-rules"><a href="#array-index-rules" class="anchor"></a>Array Index Rules</h3><p>RFC 6901 has specific rules for array indices. Section 4 states:</p><p><i>characters comprised of digits <code>...</code> that represent an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index identified by the token</i></p><p>And importantly:</p><p><i>note that leading zeros are not allowed</i></p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/0";;</x-ocaml></div><p>Zero itself is fine.</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/01";;</x-ocaml></div><p>But <code>01</code> has a leading zero, so it's NOT treated as an array index - it becomes a member name instead. This protects against accidental octal interpretation.</p><h2 id="the-end-of-array-marker:---and-type-safety"><a href="#the-end-of-array-marker:---and-type-safety" class="anchor"></a>The End-of-Array Marker: <code>-</code> and Type Safety</h2><p>RFC 6901, Section 4 introduces a special token:</p><p><i>exactly the single character "-", making the new referenced value the (nonexistent) member after the last array element.</i></p><p>This <code>-</code> marker is unique to JSON Pointer (JSON Path has no equivalent). It's primarily useful for JSON Patch operations (RFC 6902) to append elements to arrays.</p><h3 id="navigation-vs-append-pointers"><a href="#navigation-vs-append-pointers" class="anchor"></a>Navigation vs Append Pointers</h3><p>The <code>json-pointer</code> library uses <b>phantom types</b> to encode the difference between pointers that can be used for navigation and pointers that target the "append position":</p><pre>type nav (* A pointer to an existing element *)
6262+val find : nav t -> Jsont.json -> Jsont.json option</pre><h3 id="array-index-rules"><a href="#array-index-rules" class="anchor"></a>Array Index Rules</h3><p><a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a> has specific rules for array indices. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">Section 4</a> states:</p><p><i>characters comprised of digits <code>...</code> that represent an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index identified by the token</i></p><p>And importantly:</p><p><i>note that leading zeros are not allowed</i></p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/0";;</x-ocaml></div><p>Zero itself is fine.</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/01";;</x-ocaml></div><p>But <code>01</code> has a leading zero, so it's NOT treated as an array index - it becomes a member name instead. This protects against accidental octal interpretation.</p><h2 id="the-end-of-array-marker:---and-type-safety"><a href="#the-end-of-array-marker:---and-type-safety" class="anchor"></a>The End-of-Array Marker: <code>-</code> and Type Safety</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> introduces a special token:</p><p><i>exactly the single character "-", making the new referenced value the (nonexistent) member after the last array element.</i></p><p>This <code>-</code> marker is unique to JSON Pointer (JSON Path has no equivalent). It's primarily useful for JSON Patch operations (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>) to append elements to arrays.</p><h3 id="navigation-vs-append-pointers"><a href="#navigation-vs-append-pointers" class="anchor"></a>Navigation vs Append Pointers</h3><p>The <code>json-pointer</code> library uses <b>phantom types</b> to encode the difference between pointers that can be used for navigation and pointers that target the "append position":</p><pre>type nav (* A pointer to an existing element *)
6363type append (* A pointer ending with "-" (append position) *)
6464type 'a t (* Pointer with phantom type parameter *)
6565type any (* Existential: wraps either nav or append *)</pre><p>When you parse a pointer with <code>of_string</code>, you get an <code>any</code> pointer that can be used directly with mutation operations:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string "/foo/0";;
···6767add (of_string "/foo/-") arr_obj ~value:(Jsont.Json.string "c");;</x-ocaml></div><p>For retrieval operations, use <code>of_string_nav</code> which ensures the pointer doesn't contain <code>-</code>:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_nav "/foo/0";;
6868of_string_nav "/foo/-";;</x-ocaml></div><h3 id="creating-append-pointers-programmatically"><a href="#creating-append-pointers-programmatically" class="anchor"></a>Creating Append Pointers Programmatically</h3><p>You can convert a navigation pointer to an append pointer using <code>at_end</code>:</p><div class="x-ocaml-wrapper"><x-ocaml>let nav_ptr = of_string_nav "/foo";;
6969let app_ptr = at_end nav_ptr;;
7070-to_string app_ptr;;</x-ocaml></div><h2 id="mutation-operations"><a href="#mutation-operations" class="anchor"></a>Mutation Operations</h2><p>While RFC 6901 defines JSON Pointer for read-only access, RFC 6902 (JSON Patch) uses JSON Pointer for modifications. The <code>json-pointer</code> library provides these operations.</p><h3 id="add"><a href="#add" class="anchor"></a>Add</h3><p>The <code>add</code> operation inserts a value at a location. It accepts <code>any</code> pointers, so you can use <code>of_string</code> directly:</p><div class="x-ocaml-wrapper"><x-ocaml>let obj = parse_json {|{"foo":"bar"}|};;
7070+to_string app_ptr;;</x-ocaml></div><h2 id="mutation-operations"><a href="#mutation-operations" class="anchor"></a>Mutation Operations</h2><p>While <a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a> defines JSON Pointer for read-only access, <a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a> (JSON Patch) uses JSON Pointer for modifications. The <code>json-pointer</code> library provides these operations.</p><h3 id="add"><a href="#add" class="anchor"></a>Add</h3><p>The <code>add</code> operation inserts a value at a location. It accepts <code>any</code> pointers, so you can use <code>of_string</code> directly:</p><div class="x-ocaml-wrapper"><x-ocaml>let obj = parse_json {|{"foo":"bar"}|};;
7171add (of_string "/baz") obj ~value:(Jsont.Json.string "qux");;</x-ocaml></div><p>For arrays, <code>add</code> inserts BEFORE the specified index:</p><div class="x-ocaml-wrapper"><x-ocaml>let arr_obj = parse_json {|{"foo":["a","b"]}|};;
7272add (of_string "/foo/1") arr_obj ~value:(Jsont.Json.string "X");;</x-ocaml></div><p>This is where the <code>-</code> marker shines - it appends to the end:</p><div class="x-ocaml-wrapper"><x-ocaml>add (of_string "/foo/-") arr_obj ~value:(Jsont.Json.string "c");;</x-ocaml></div><p>You can also use <code>at_end</code> to create an append pointer programmatically:</p><div class="x-ocaml-wrapper"><x-ocaml>add (any (at_end (of_string_nav "/foo"))) arr_obj ~value:(Jsont.Json.string "c");;</x-ocaml></div><h3 id="ergonomic-mutation-with-any"><a href="#ergonomic-mutation-with-any" class="anchor"></a>Ergonomic Mutation with <code>any</code></h3><p>Since <code>add</code>, <code>set</code>, <code>move</code>, and <code>copy</code> accept <code>any</code> pointers, you can use <code>of_string</code> directly without any pattern matching. This makes JSON Patch implementations straightforward:</p><div class="x-ocaml-wrapper"><x-ocaml>let items = parse_json {|{"items":["x"]}|};;
7373add (of_string "/items/0") items ~value:(Jsont.Json.string "y");;
···7777 ;;</x-ocaml></div><p>Unlike <code>add</code>, <code>replace</code> requires the target to already exist (hence <code>nav t</code>). Attempting to replace a nonexistent path raises an error.</p><h3 id="move"><a href="#move" class="anchor"></a>Move</h3><p>The <code>move</code> operation relocates a value. The source (<code>from</code>) must be a <code>nav t</code> (you can only move something that exists), but the destination (<code>path</code>) accepts <code>any</code>:</p><div class="x-ocaml-wrapper"><x-ocaml>let nested = parse_json {|{"foo":{"bar":"baz"},"qux":{}}|};;
7878move ~from:(of_string_nav "/foo/bar") ~path:(of_string "/qux/thud") nested;;</x-ocaml></div><h3 id="copy"><a href="#copy" class="anchor"></a>Copy</h3><p>The <code>copy</code> operation duplicates a value (same typing as <code>move</code>):</p><div class="x-ocaml-wrapper"><x-ocaml>let to_copy = parse_json {|{"foo":{"bar":"baz"}}|};;
7979copy ~from:(of_string_nav "/foo/bar") ~path:(of_string "/foo/qux") to_copy;;</x-ocaml></div><h3 id="test"><a href="#test" class="anchor"></a>Test</h3><p>The <code>test</code> operation verifies a value (useful in JSON Patch):</p><div class="x-ocaml-wrapper"><x-ocaml>test (of_string_nav "/foo") obj ~expected:(Jsont.Json.string "bar");;
8080-test (of_string_nav "/foo") obj ~expected:(Jsont.Json.string "wrong");;</x-ocaml></div><h2 id="escaping"><a href="#escaping" class="anchor"></a>Escaping Special Characters</h2><p>RFC 6901, Section 3 explains the escaping rules:</p><p><i>Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference token.</i></p><p>Why these specific characters?</p><ul><li><code>/</code> separates tokens, so it must be escaped inside a token</li><li><code>~</code> is the escape character itself, so it must also be escaped</li></ul><p>The escape sequences are:</p><ul><li><code>~0</code> represents <code>~</code> (tilde)</li><li><code>~1</code> represents <code>/</code> (forward slash)</li></ul><h3 id="the-library-handles-escaping-automatically"><a href="#the-library-handles-escaping-automatically" class="anchor"></a>The Library Handles Escaping Automatically</h3><p><b>Important</b>: When using <code>json-pointer</code> programmatically, you rarely need to think about escaping. The <code>Mem</code> variant stores unescaped strings, and escaping happens automatically during serialization:</p><div class="x-ocaml-wrapper"><x-ocaml>let p = make [mem "a/b"];;
8080+test (of_string_nav "/foo") obj ~expected:(Jsont.Json.string "wrong");;</x-ocaml></div><h2 id="escaping"><a href="#escaping" class="anchor"></a>Escaping Special Characters</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-3">RFC 6901, Section 3</a> explains the escaping rules:</p><p><i>Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference token.</i></p><p>Why these specific characters?</p><ul><li><code>/</code> separates tokens, so it must be escaped inside a token</li><li><code>~</code> is the escape character itself, so it must also be escaped</li></ul><p>The escape sequences are:</p><ul><li><code>~0</code> represents <code>~</code> (tilde)</li><li><code>~1</code> represents <code>/</code> (forward slash)</li></ul><h3 id="the-library-handles-escaping-automatically"><a href="#the-library-handles-escaping-automatically" class="anchor"></a>The Library Handles Escaping Automatically</h3><p><b>Important</b>: When using <code>json-pointer</code> programmatically, you rarely need to think about escaping. The <code>Mem</code> variant stores unescaped strings, and escaping happens automatically during serialization:</p><div class="x-ocaml-wrapper"><x-ocaml>let p = make [mem "a/b"];;
8181to_string p;;
8282of_string_nav "/a~1b";;</x-ocaml></div><h3 id="escaping-in-action"><a href="#escaping-in-action" class="anchor"></a>Escaping in Action</h3><p>The <code>Token</code> module exposes the escaping functions:</p><div class="x-ocaml-wrapper"><x-ocaml>Token.escape "hello";;
8383Token.escape "a/b";;
8484Token.escape "a~b";;
8585Token.escape "~/";;</x-ocaml></div><h3 id="unescaping"><a href="#unescaping" class="anchor"></a>Unescaping</h3><p>And the reverse process:</p><div class="x-ocaml-wrapper"><x-ocaml>Token.unescape "a~1b";;
8686-Token.unescape "a~0b";;</x-ocaml></div><h3 id="the-order-matters!"><a href="#the-order-matters!" class="anchor"></a>The Order Matters!</h3><p>RFC 6901, Section 4 is careful to specify the unescaping order:</p><p><i>Evaluation of each reference token begins by decoding any escaped character sequence. This is performed by first transforming any occurrence of the sequence '~1' to '/', and then transforming any occurrence of the sequence '~0' to '~'. By performing the substitutions in this order, an implementation avoids the error of turning '~01' first into '~1' and then into '/', which would be incorrect (the string '~01' correctly becomes '~1' after transformation).</i></p><p>Let's verify this tricky case:</p><div class="x-ocaml-wrapper"><x-ocaml>Token.unescape "~01";;</x-ocaml></div><p>If we unescaped <code>~0</code> first, <code>~01</code> would become <code>~1</code>, which would then become <code>/</code>. But that's wrong! The sequence <code>~01</code> should become the literal string <code>~1</code> (a tilde followed by the digit one).</p><h2 id="uri-fragment-encoding"><a href="#uri-fragment-encoding" class="anchor"></a>URI Fragment Encoding</h2><p>JSON Pointers can be embedded in URIs. RFC 6901, Section 6 explains:</p><p><i>A JSON Pointer can be represented in a URI fragment identifier by encoding it into octets using UTF-8, while percent-encoding those characters not allowed by the fragment rule in RFC 3986.</i></p><p>This adds percent-encoding on top of the <code>~0</code>/<code>~1</code> escaping:</p><div class="x-ocaml-wrapper"><x-ocaml>to_uri_fragment (of_string_nav "/foo");;
8686+Token.unescape "a~0b";;</x-ocaml></div><h3 id="the-order-matters!"><a href="#the-order-matters!" class="anchor"></a>The Order Matters!</h3><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> is careful to specify the unescaping order:</p><p><i>Evaluation of each reference token begins by decoding any escaped character sequence. This is performed by first transforming any occurrence of the sequence '~1' to '/', and then transforming any occurrence of the sequence '~0' to '~'. By performing the substitutions in this order, an implementation avoids the error of turning '~01' first into '~1' and then into '/', which would be incorrect (the string '~01' correctly becomes '~1' after transformation).</i></p><p>Let's verify this tricky case:</p><div class="x-ocaml-wrapper"><x-ocaml>Token.unescape "~01";;</x-ocaml></div><p>If we unescaped <code>~0</code> first, <code>~01</code> would become <code>~1</code>, which would then become <code>/</code>. But that's wrong! The sequence <code>~01</code> should become the literal string <code>~1</code> (a tilde followed by the digit one).</p><h2 id="uri-fragment-encoding"><a href="#uri-fragment-encoding" class="anchor"></a>URI Fragment Encoding</h2><p>JSON Pointers can be embedded in URIs. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-6">RFC 6901, Section 6</a> explains:</p><p><i>A JSON Pointer can be represented in a URI fragment identifier by encoding it into octets using UTF-8, while percent-encoding those characters not allowed by the fragment rule in <a href="https://datatracker.ietf.org/doc/html/rfc3986">RFC 3986</a>.</i></p><p>This adds percent-encoding on top of the <code>~0</code>/<code>~1</code> escaping:</p><div class="x-ocaml-wrapper"><x-ocaml>to_uri_fragment (of_string_nav "/foo");;
8787to_uri_fragment (of_string_nav "/a~1b");;
8888to_uri_fragment (of_string_nav "/c%d");;
8989to_uri_fragment (of_string_nav "/ ");;</x-ocaml></div><p>The <code>%</code> character must be percent-encoded as <code>%25</code> in URIs, and spaces become <code>%20</code>.</p><p>Here's the RFC example showing the URI fragment forms:</p><ul><li><code>""</code> -> <code>#</code> -> whole document</li><li><code>"/foo"</code> -> <code>#/foo</code> -> <code>["bar", "baz"]</code></li><li><code>"/foo/0"</code> -> <code>#/foo/0</code> -> <code>"bar"</code></li><li><code>"/"</code> -> <code>#/</code> -> <code>0</code></li><li><code>"/a~1b"</code> -> <code>#/a~1b</code> -> <code>1</code></li><li><code>"/c%d"</code> -> <code>#/c%25d</code> -> <code>2</code></li><li><code>"/ "</code> -> <code>#/%20</code> -> <code>7</code></li><li><code>"/m~0n"</code> -> <code>#/m~0n</code> -> <code>8</code></li></ul><h2 id="building-pointers-programmatically"><a href="#building-pointers-programmatically" class="anchor"></a>Building Pointers Programmatically</h2><p>Instead of parsing strings, you can build pointers from indices:</p><div class="x-ocaml-wrapper"><x-ocaml>let port_ptr = make [mem "database"; mem "port"];;
···140140 config_json
141141 |> Result.get_ok;;</x-ocaml></div><p>The typed approach catches mismatches at decode time with clear errors.</p><h3 id="updates-with-polymorphic-pointers"><a href="#updates-with-polymorphic-pointers" class="anchor"></a>Updates with Polymorphic Pointers</h3><p>The <code>set</code> and <code>add</code> functions accept <code>any</code> pointers, which means you can use the result of <code>of_string</code> directly without pattern matching:</p><div class="x-ocaml-wrapper"><x-ocaml>let tasks = parse_json {|{"tasks":["buy milk"]}|};;
142142set (of_string "/tasks/0") tasks ~value:(Jsont.Json.string "buy eggs");;
143143-set (of_string "/tasks/-") tasks ~value:(Jsont.Json.string "call mom");;</x-ocaml></div><p>This is useful for implementing JSON Patch (<code>RFC 6902</code>) where operations like <code>"add"</code> can target either existing positions or the append marker. If you need to distinguish between pointer types at runtime, use <code>of_string_kind</code> which returns a polymorphic variant:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_kind "/tasks/0";;
144144-of_string_kind "/tasks/-";;</x-ocaml></div><h2 id="summary"><a href="#summary" class="anchor"></a>Summary</h2><p>JSON Pointer (RFC 6901) provides a simple but powerful way to address values within JSON documents:</p><ol><li><b>Syntax</b>: Pointers are strings of <code>/</code>-separated reference tokens</li><li><b>Escaping</b>: Use <code>~0</code> for <code>~</code> and <code>~1</code> for <code>/</code> in tokens (handled automatically by the library)</li><li><b>Evaluation</b>: Tokens navigate through objects (by key) and arrays (by index)</li><li><b>URI Encoding</b>: Pointers can be percent-encoded for use in URIs</li><li><b>Mutations</b>: Combined with JSON Patch (RFC 6902), pointers enable structured updates</li><li><b>Type Safety</b>: Phantom types (<code>nav t</code> vs <code>append t</code>) prevent misuse of append pointers with retrieval operations, while the <code>any</code> existential type allows ergonomic use with mutation operations</li></ol><p>The <code>json-pointer</code> library implements all of this with type-safe OCaml interfaces, integration with the <code>jsont</code> codec system, and proper error handling for malformed pointers and missing values.</p><h3 id="key-points-on-json-pointer-vs-json-path"><a href="#key-points-on-json-pointer-vs-json-path" class="anchor"></a>Key Points on JSON Pointer vs JSON Path</h3><ul><li><b>JSON Pointer</b> addresses a <em>single</em> location (like a file path)</li><li><b>JSON Path</b> queries for <em>multiple</em> values (like a search)</li><li>The <code>-</code> token is unique to JSON Pointer - it means "append position" for arrays</li><li>The library uses phantom types to enforce that <code>-</code> (append) pointers cannot be used with <code>get</code>/<code>find</code></li></ul></div>
143143+set (of_string "/tasks/-") tasks ~value:(Jsont.Json.string "call mom");;</x-ocaml></div><p>This is useful for implementing JSON Patch (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>) where operations like <code>"add"</code> can target either existing positions or the append marker. If you need to distinguish between pointer types at runtime, use <code>of_string_kind</code> which returns a polymorphic variant:</p><div class="x-ocaml-wrapper"><x-ocaml>of_string_kind "/tasks/0";;
144144+of_string_kind "/tasks/-";;</x-ocaml></div><h2 id="summary"><a href="#summary" class="anchor"></a>Summary</h2><p>JSON Pointer (<a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a>) provides a simple but powerful way to address values within JSON documents:</p><ol><li><b>Syntax</b>: Pointers are strings of <code>/</code>-separated reference tokens</li><li><b>Escaping</b>: Use <code>~0</code> for <code>~</code> and <code>~1</code> for <code>/</code> in tokens (handled automatically by the library)</li><li><b>Evaluation</b>: Tokens navigate through objects (by key) and arrays (by index)</li><li><b>URI Encoding</b>: Pointers can be percent-encoded for use in URIs</li><li><b>Mutations</b>: Combined with JSON Patch (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>), pointers enable structured updates</li><li><b>Type Safety</b>: Phantom types (<code>nav t</code> vs <code>append t</code>) prevent misuse of append pointers with retrieval operations, while the <code>any</code> existential type allows ergonomic use with mutation operations</li></ol><p>The <code>json-pointer</code> library implements all of this with type-safe OCaml interfaces, integration with the <code>jsont</code> codec system, and proper error handling for malformed pointers and missing values.</p><h3 id="key-points-on-json-pointer-vs-json-path"><a href="#key-points-on-json-pointer-vs-json-path" class="anchor"></a>Key Points on JSON Pointer vs JSON Path</h3><ul><li><b>JSON Pointer</b> addresses a <em>single</em> location (like a file path)</li><li><b>JSON Path</b> queries for <em>multiple</em> values (like a search)</li><li>The <code>-</code> token is unique to JSON Pointer - it means "append position" for arrays</li><li>The library uses phantom types to enforce that <code>-</code> (append) pointers cannot be used with <code>get</code>/<code>find</code></li></ul></div>
145145</body></html>
+12-12
doc/tutorial.html
···11<!DOCTYPE html>
22-<html xmlns="http://www.w3.org/1999/xhtml"><head><title>tutorial (tutorial)</title><meta charset="utf-8"/><link rel="stylesheet" href="odoc.css"/><meta name="generator" content="odoc 3.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> – <a href="index.html">Index</a> » tutorial</nav><header class="odoc-preamble"><h1 id="json-pointer-tutorial"><a href="#json-pointer-tutorial" class="anchor"></a>JSON Pointer Tutorial</h1><p>This tutorial introduces JSON Pointer as defined in <a href="https://www.rfc-editor.org/rfc/rfc6901">RFC 6901</a>, and demonstrates the <code>json-pointer</code> OCaml library through interactive examples.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#json-pointer-vs-json-path">JSON Pointer vs JSON Path</a></li><li><a href="#setup">Setup</a></li><li><a href="#what-is-json-pointer?">What is JSON Pointer?</a></li><li><a href="#syntax:-reference-tokens">Syntax: Reference Tokens</a><ul><li><a href="#the-index-type">The Index Type</a></li><li><a href="#invalid-syntax">Invalid Syntax</a></li></ul></li><li><a href="#evaluation:-navigating-json">Evaluation: Navigating JSON</a><ul><li><a href="#the-root-pointer">The Root Pointer</a></li><li><a href="#object-member-access">Object Member Access</a></li><li><a href="#array-index-access">Array Index Access</a></li><li><a href="#empty-string-as-key">Empty String as Key</a></li><li><a href="#keys-with-special-characters">Keys with Special Characters</a></li><li><a href="#other-special-characters-(no-escaping-needed)">Other Special Characters (No Escaping Needed)</a></li><li><a href="#error-conditions">Error Conditions</a></li><li><a href="#array-index-rules">Array Index Rules</a></li></ul></li><li><a href="#the-end-of-array-marker:---and-type-safety">The End-of-Array Marker: <code>-</code> and Type Safety</a><ul><li><a href="#navigation-vs-append-pointers">Navigation vs Append Pointers</a></li><li><a href="#why-two-pointer-types?">Why Two Pointer Types?</a></li><li><a href="#creating-append-pointers-programmatically">Creating Append Pointers Programmatically</a></li></ul></li><li><a href="#mutation-operations">Mutation Operations</a><ul><li><a href="#add">Add</a></li><li><a href="#ergonomic-mutation-with-any">Ergonomic Mutation with <code>any</code></a></li><li><a href="#remove">Remove</a></li><li><a href="#replace">Replace</a></li><li><a href="#move">Move</a></li><li><a href="#copy">Copy</a></li><li><a href="#test">Test</a></li></ul></li><li><a href="#escaping">Escaping Special Characters</a><ul><li><a href="#the-library-handles-escaping-automatically">The Library Handles Escaping Automatically</a></li><li><a href="#escaping-in-action">Escaping in Action</a></li><li><a href="#unescaping">Unescaping</a></li><li><a href="#the-order-matters!">The Order Matters!</a></li></ul></li><li><a href="#uri-fragment-encoding">URI Fragment Encoding</a></li><li><a href="#building-pointers-programmatically">Building Pointers Programmatically</a><ul><li><a href="#pointer-navigation">Pointer Navigation</a></li></ul></li><li><a href="#jsont-integration">Jsont Integration</a><ul><li><a href="#typed-access-with-path">Typed Access with <code>path</code></a></li><li><a href="#default-values-with-~absent">Default Values with <code>~absent</code></a></li><li><a href="#nested-path-extraction">Nested Path Extraction</a></li><li><a href="#comparison:-raw-vs-typed-access">Comparison: Raw vs Typed Access</a></li><li><a href="#updates-with-polymorphic-pointers">Updates with Polymorphic Pointers</a></li></ul></li><li><a href="#summary">Summary</a><ul><li><a href="#key-points-on-json-pointer-vs-json-path">Key Points on JSON Pointer vs JSON Path</a></li></ul></li></ul></nav></div><div class="odoc-content"><h2 id="json-pointer-vs-json-path"><a href="#json-pointer-vs-json-path" class="anchor"></a>JSON Pointer vs JSON Path</h2><p>Before diving in, it's worth understanding the difference between JSON Pointer and JSON Path, as they serve different purposes:</p><p><b>JSON Pointer</b> (RFC 6901) is an <em>indicator syntax</em> that specifies a <em>single location</em> within JSON data. It always identifies at most one value.</p><p><b>JSON Path</b> is a <em>query syntax</em> that can <em>search</em> JSON data and return <em>multiple</em> values matching specified criteria.</p><p>Use JSON Pointer when you need to address a single, specific location (like JSON Schema's <code>$ref</code>). Use JSON Path when you might need multiple results (like Kubernetes queries).</p><p>The <code>json-pointer</code> library implements JSON Pointer and integrates with the <code>Jsont.Path</code> type for representing navigation indices.</p><h2 id="setup"><a href="#setup" class="anchor"></a>Setup</h2><p>First, let's set up our environment. In the toplevel, you can load the library with <code>#require "json-pointer.top";;</code> which will automatically install pretty printers.</p><pre class="language-ocaml"><code># Json_pointer_top.install ();;
22+<html xmlns="http://www.w3.org/1999/xhtml"><head><title>tutorial (tutorial)</title><meta charset="utf-8"/><link rel="stylesheet" href="odoc.css"/><meta name="generator" content="odoc 3.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> – <a href="index.html">Index</a> » tutorial</nav><header class="odoc-preamble"><h1 id="json-pointer-tutorial"><a href="#json-pointer-tutorial" class="anchor"></a>JSON Pointer Tutorial</h1><p>This tutorial introduces JSON Pointer as defined in <a href="https://www.rfc-editor.org/rfc/rfc6901">RFC 6901</a>, and demonstrates the <code>json-pointer</code> OCaml library through interactive examples.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#json-pointer-vs-json-path">JSON Pointer vs JSON Path</a></li><li><a href="#setup">Setup</a></li><li><a href="#what-is-json-pointer?">What is JSON Pointer?</a></li><li><a href="#syntax:-reference-tokens">Syntax: Reference Tokens</a><ul><li><a href="#the-index-type">The Index Type</a></li><li><a href="#invalid-syntax">Invalid Syntax</a></li></ul></li><li><a href="#evaluation:-navigating-json">Evaluation: Navigating JSON</a><ul><li><a href="#the-root-pointer">The Root Pointer</a></li><li><a href="#object-member-access">Object Member Access</a></li><li><a href="#array-index-access">Array Index Access</a></li><li><a href="#empty-string-as-key">Empty String as Key</a></li><li><a href="#keys-with-special-characters">Keys with Special Characters</a></li><li><a href="#other-special-characters-(no-escaping-needed)">Other Special Characters (No Escaping Needed)</a></li><li><a href="#error-conditions">Error Conditions</a></li><li><a href="#array-index-rules">Array Index Rules</a></li></ul></li><li><a href="#the-end-of-array-marker:---and-type-safety">The End-of-Array Marker: <code>-</code> and Type Safety</a><ul><li><a href="#navigation-vs-append-pointers">Navigation vs Append Pointers</a></li><li><a href="#why-two-pointer-types?">Why Two Pointer Types?</a></li><li><a href="#creating-append-pointers-programmatically">Creating Append Pointers Programmatically</a></li></ul></li><li><a href="#mutation-operations">Mutation Operations</a><ul><li><a href="#add">Add</a></li><li><a href="#ergonomic-mutation-with-any">Ergonomic Mutation with <code>any</code></a></li><li><a href="#remove">Remove</a></li><li><a href="#replace">Replace</a></li><li><a href="#move">Move</a></li><li><a href="#copy">Copy</a></li><li><a href="#test">Test</a></li></ul></li><li><a href="#escaping">Escaping Special Characters</a><ul><li><a href="#the-library-handles-escaping-automatically">The Library Handles Escaping Automatically</a></li><li><a href="#escaping-in-action">Escaping in Action</a></li><li><a href="#unescaping">Unescaping</a></li><li><a href="#the-order-matters!">The Order Matters!</a></li></ul></li><li><a href="#uri-fragment-encoding">URI Fragment Encoding</a></li><li><a href="#building-pointers-programmatically">Building Pointers Programmatically</a><ul><li><a href="#pointer-navigation">Pointer Navigation</a></li></ul></li><li><a href="#jsont-integration">Jsont Integration</a><ul><li><a href="#typed-access-with-path">Typed Access with <code>path</code></a></li><li><a href="#default-values-with-~absent">Default Values with <code>~absent</code></a></li><li><a href="#nested-path-extraction">Nested Path Extraction</a></li><li><a href="#comparison:-raw-vs-typed-access">Comparison: Raw vs Typed Access</a></li><li><a href="#updates-with-polymorphic-pointers">Updates with Polymorphic Pointers</a></li></ul></li><li><a href="#summary">Summary</a><ul><li><a href="#key-points-on-json-pointer-vs-json-path">Key Points on JSON Pointer vs JSON Path</a></li></ul></li></ul></nav></div><div class="odoc-content"><h2 id="json-pointer-vs-json-path"><a href="#json-pointer-vs-json-path" class="anchor"></a>JSON Pointer vs JSON Path</h2><p>Before diving in, it's worth understanding the difference between JSON Pointer and JSON Path, as they serve different purposes:</p><p><b>JSON Pointer</b> (<a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a>) is an <em>indicator syntax</em> that specifies a <em>single location</em> within JSON data. It always identifies at most one value.</p><p><b>JSON Path</b> is a <em>query syntax</em> that can <em>search</em> JSON data and return <em>multiple</em> values matching specified criteria.</p><p>Use JSON Pointer when you need to address a single, specific location (like JSON Schema's <code>$ref</code>). Use JSON Path when you might need multiple results (like Kubernetes queries).</p><p>The <code>json-pointer</code> library implements JSON Pointer and integrates with the <code>Jsont.Path</code> type for representing navigation indices.</p><h2 id="setup"><a href="#setup" class="anchor"></a>Setup</h2><p>First, let's set up our environment. In the toplevel, you can load the library with <code>#require "json-pointer.top";;</code> which will automatically install pretty printers.</p><pre class="language-ocaml"><code># Json_pointer_top.install ();;
33- : unit = ()
44# open Json_pointer;;
55# let parse_json s =
66 match Jsont_bytesrw.decode_string Jsont.json s with
77 | Ok json -> json
88 | Error e -> failwith e;;
99-val parse_json : string -> Jsont.json = <fun></code></pre><h2 id="what-is-json-pointer?"><a href="#what-is-json-pointer?" class="anchor"></a>What is JSON Pointer?</h2><p>From RFC 6901, Section 1:</p><p><i>JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.</i></p><p>In other words, JSON Pointer is an addressing scheme for locating values inside a JSON structure. Think of it like a filesystem path, but for JSON documents instead of files.</p><p>For example, given this JSON document:</p><pre class="language-ocaml"><code># let users_json = parse_json {|{
99+val parse_json : string -> Jsont.json = <fun></code></pre><h2 id="what-is-json-pointer?"><a href="#what-is-json-pointer?" class="anchor"></a>What is JSON Pointer?</h2><p>From <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-1">RFC 6901, Section 1</a>:</p><p><i>JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.</i></p><p>In other words, JSON Pointer is an addressing scheme for locating values inside a JSON structure. Think of it like a filesystem path, but for JSON documents instead of files.</p><p>For example, given this JSON document:</p><pre class="language-ocaml"><code># let users_json = parse_json {|{
1010 "users": [
1111 {"name": "Alice", "age": 30},
1212 {"name": "Bob", "age": 25}
···1616 {"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}</code></pre><p>The JSON Pointer <code>/users/0/name</code> refers to the string <code>"Alice"</code>:</p><pre class="language-ocaml"><code># let ptr = of_string_nav "/users/0/name";;
1717val ptr : nav t = [Mem "users"; Nth 0; Mem "name"]
1818# get ptr users_json;;
1919-- : Jsont.json = "Alice"</code></pre><p>In OCaml, this is represented by the <code>'a Json_pointer.t</code> type - a sequence of navigation steps from the document root to a target value. The phantom type parameter <code>'a</code> encodes whether this is a navigation pointer or an append pointer (more on this later).</p><h2 id="syntax:-reference-tokens"><a href="#syntax:-reference-tokens" class="anchor"></a>Syntax: Reference Tokens</h2><p>RFC 6901, Section 3 defines the syntax:</p><p><i>A JSON Pointer is a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a '/' (%x2F) character.</i></p><p>The grammar is elegantly simple:</p><pre>json-pointer = *( "/" reference-token )
1919+- : Jsont.json = "Alice"</code></pre><p>In OCaml, this is represented by the <code>'a Json_pointer.t</code> type - a sequence of navigation steps from the document root to a target value. The phantom type parameter <code>'a</code> encodes whether this is a navigation pointer or an append pointer (more on this later).</p><h2 id="syntax:-reference-tokens"><a href="#syntax:-reference-tokens" class="anchor"></a>Syntax: Reference Tokens</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-3">RFC 6901, Section 3</a> defines the syntax:</p><p><i>A JSON Pointer is a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a '/' (%x2F) character.</i></p><p>The grammar is elegantly simple:</p><pre>json-pointer = *( "/" reference-token )
2020reference-token = *( unescaped / escaped )</pre><p>This means:</p><ul><li>The empty string <code>""</code> is a valid pointer (it refers to the whole document)</li><li>Every non-empty pointer starts with <code>/</code></li><li>Everything between <code>/</code> characters is a "reference token"</li></ul><p>Let's see this in action:</p><pre class="language-ocaml"><code># of_string_nav "";;
2121- : nav t = []</code></pre><p>The empty pointer has no reference tokens - it points to the root.</p><pre class="language-ocaml"><code># of_string_nav "/foo";;
2222- : nav t = [Mem "foo"]</code></pre><p>The pointer <code>/foo</code> has one token: <code>foo</code>. Since it's not a number, it's interpreted as an object member name (<code>Mem</code>).</p><pre class="language-ocaml"><code># of_string_nav "/foo/0";;
···2929- : (any, string) result =
3030Error "Invalid JSON Pointer: must be empty or start with '/': foo"
3131# of_string_result "/valid";;
3232-- : (any, string) result = Ok (Any <abstr>)</code></pre><h2 id="evaluation:-navigating-json"><a href="#evaluation:-navigating-json" class="anchor"></a>Evaluation: Navigating JSON</h2><p>Now we come to the heart of JSON Pointer: evaluation. RFC 6901, Section 4 describes how a pointer is resolved against a JSON document:</p><p><i>Evaluation of a JSON Pointer begins with a reference to the root value of a JSON document and completes with a reference to some value within the document. Each reference token in the JSON Pointer is evaluated sequentially.</i></p><p>Let's use the example JSON document from RFC 6901, Section 5:</p><pre class="language-ocaml"><code># let rfc_example = parse_json {|{
3232+- : (any, string) result = Ok (Any <abstr>)</code></pre><h2 id="evaluation:-navigating-json"><a href="#evaluation:-navigating-json" class="anchor"></a>Evaluation: Navigating JSON</h2><p>Now we come to the heart of JSON Pointer: evaluation. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> describes how a pointer is resolved against a JSON document:</p><p><i>Evaluation of a JSON Pointer begins with a reference to the root value of a JSON document and completes with a reference to some value within the document. Each reference token in the JSON Pointer is evaluated sequentially.</i></p><p>Let's use the example JSON document from <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-5">RFC 6901, Section 5</a>:</p><pre class="language-ocaml"><code># let rfc_example = parse_json {|{
3333 "foo": ["bar", "baz"],
3434 "": 0,
3535 "a/b": 1,
···7272- : Jsont.json option = None</code></pre><p>Or try to index into a non-container:</p><pre class="language-ocaml"><code># find (of_string_nav "/foo/0/invalid") rfc_example;;
7373- : Jsont.json option = None</code></pre><p>The library provides both exception-raising and result-returning variants:</p><pre>val get : nav t -> Jsont.json -> Jsont.json
7474val get_result : nav t -> Jsont.json -> (Jsont.json, Jsont.Error.t) result
7575-val find : nav t -> Jsont.json -> Jsont.json option</pre><h3 id="array-index-rules"><a href="#array-index-rules" class="anchor"></a>Array Index Rules</h3><p>RFC 6901 has specific rules for array indices. Section 4 states:</p><p><i>characters comprised of digits <code>...</code> that represent an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index identified by the token</i></p><p>And importantly:</p><p><i>note that leading zeros are not allowed</i></p><pre class="language-ocaml"><code># of_string_nav "/foo/0";;
7575+val find : nav t -> Jsont.json -> Jsont.json option</pre><h3 id="array-index-rules"><a href="#array-index-rules" class="anchor"></a>Array Index Rules</h3><p><a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a> has specific rules for array indices. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">Section 4</a> states:</p><p><i>characters comprised of digits <code>...</code> that represent an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index identified by the token</i></p><p>And importantly:</p><p><i>note that leading zeros are not allowed</i></p><pre class="language-ocaml"><code># of_string_nav "/foo/0";;
7676- : nav t = [Mem "foo"; Nth 0]</code></pre><p>Zero itself is fine.</p><pre class="language-ocaml"><code># of_string_nav "/foo/01";;
7777-- : nav t = [Mem "foo"; Mem "01"]</code></pre><p>But <code>01</code> has a leading zero, so it's NOT treated as an array index - it becomes a member name instead. This protects against accidental octal interpretation.</p><h2 id="the-end-of-array-marker:---and-type-safety"><a href="#the-end-of-array-marker:---and-type-safety" class="anchor"></a>The End-of-Array Marker: <code>-</code> and Type Safety</h2><p>RFC 6901, Section 4 introduces a special token:</p><p><i>exactly the single character "-", making the new referenced value the (nonexistent) member after the last array element.</i></p><p>This <code>-</code> marker is unique to JSON Pointer (JSON Path has no equivalent). It's primarily useful for JSON Patch operations (RFC 6902) to append elements to arrays.</p><h3 id="navigation-vs-append-pointers"><a href="#navigation-vs-append-pointers" class="anchor"></a>Navigation vs Append Pointers</h3><p>The <code>json-pointer</code> library uses <b>phantom types</b> to encode the difference between pointers that can be used for navigation and pointers that target the "append position":</p><pre>type nav (* A pointer to an existing element *)
7777+- : nav t = [Mem "foo"; Mem "01"]</code></pre><p>But <code>01</code> has a leading zero, so it's NOT treated as an array index - it becomes a member name instead. This protects against accidental octal interpretation.</p><h2 id="the-end-of-array-marker:---and-type-safety"><a href="#the-end-of-array-marker:---and-type-safety" class="anchor"></a>The End-of-Array Marker: <code>-</code> and Type Safety</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> introduces a special token:</p><p><i>exactly the single character "-", making the new referenced value the (nonexistent) member after the last array element.</i></p><p>This <code>-</code> marker is unique to JSON Pointer (JSON Path has no equivalent). It's primarily useful for JSON Patch operations (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>) to append elements to arrays.</p><h3 id="navigation-vs-append-pointers"><a href="#navigation-vs-append-pointers" class="anchor"></a>Navigation vs Append Pointers</h3><p>The <code>json-pointer</code> library uses <b>phantom types</b> to encode the difference between pointers that can be used for navigation and pointers that target the "append position":</p><pre>type nav (* A pointer to an existing element *)
7878type append (* A pointer ending with "-" (append position) *)
7979type 'a t (* Pointer with phantom type parameter *)
8080type any (* Existential: wraps either nav or append *)</pre><p>When you parse a pointer with <code>of_string</code>, you get an <code>any</code> pointer that can be used directly with mutation operations:</p><pre class="language-ocaml"><code># of_string "/foo/0";;
···9292# let app_ptr = at_end nav_ptr;;
9393val app_ptr : append t = [Mem "foo"] /-
9494# to_string app_ptr;;
9595-- : string = "/foo/-"</code></pre><h2 id="mutation-operations"><a href="#mutation-operations" class="anchor"></a>Mutation Operations</h2><p>While RFC 6901 defines JSON Pointer for read-only access, RFC 6902 (JSON Patch) uses JSON Pointer for modifications. The <code>json-pointer</code> library provides these operations.</p><h3 id="add"><a href="#add" class="anchor"></a>Add</h3><p>The <code>add</code> operation inserts a value at a location. It accepts <code>any</code> pointers, so you can use <code>of_string</code> directly:</p><pre class="language-ocaml"><code># let obj = parse_json {|{"foo":"bar"}|};;
9595+- : string = "/foo/-"</code></pre><h2 id="mutation-operations"><a href="#mutation-operations" class="anchor"></a>Mutation Operations</h2><p>While <a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a> defines JSON Pointer for read-only access, <a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a> (JSON Patch) uses JSON Pointer for modifications. The <code>json-pointer</code> library provides these operations.</p><h3 id="add"><a href="#add" class="anchor"></a>Add</h3><p>The <code>add</code> operation inserts a value at a location. It accepts <code>any</code> pointers, so you can use <code>of_string</code> directly:</p><pre class="language-ocaml"><code># let obj = parse_json {|{"foo":"bar"}|};;
9696val obj : Jsont.json = {"foo":"bar"}
9797# add (of_string "/baz") obj ~value:(Jsont.Json.string "qux");;
9898- : Jsont.json = {"foo":"bar","baz":"qux"}</code></pre><p>For arrays, <code>add</code> inserts BEFORE the specified index:</p><pre class="language-ocaml"><code># let arr_obj = parse_json {|{"foo":["a","b"]}|};;
···122122- : Jsont.json = {"foo":{"bar":"baz","qux":"baz"}}</code></pre><h3 id="test"><a href="#test" class="anchor"></a>Test</h3><p>The <code>test</code> operation verifies a value (useful in JSON Patch):</p><pre class="language-ocaml"><code># test (of_string_nav "/foo") obj ~expected:(Jsont.Json.string "bar");;
123123- : bool = true
124124# test (of_string_nav "/foo") obj ~expected:(Jsont.Json.string "wrong");;
125125-- : bool = false</code></pre><h2 id="escaping"><a href="#escaping" class="anchor"></a>Escaping Special Characters</h2><p>RFC 6901, Section 3 explains the escaping rules:</p><p><i>Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference token.</i></p><p>Why these specific characters?</p><ul><li><code>/</code> separates tokens, so it must be escaped inside a token</li><li><code>~</code> is the escape character itself, so it must also be escaped</li></ul><p>The escape sequences are:</p><ul><li><code>~0</code> represents <code>~</code> (tilde)</li><li><code>~1</code> represents <code>/</code> (forward slash)</li></ul><h3 id="the-library-handles-escaping-automatically"><a href="#the-library-handles-escaping-automatically" class="anchor"></a>The Library Handles Escaping Automatically</h3><p><b>Important</b>: When using <code>json-pointer</code> programmatically, you rarely need to think about escaping. The <code>Mem</code> variant stores unescaped strings, and escaping happens automatically during serialization:</p><pre class="language-ocaml"><code># let p = make [mem "a/b"];;
125125+- : bool = false</code></pre><h2 id="escaping"><a href="#escaping" class="anchor"></a>Escaping Special Characters</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-3">RFC 6901, Section 3</a> explains the escaping rules:</p><p><i>Because the characters '~' (%x7E) and '/' (%x2F) have special meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/' needs to be encoded as '~1' when these characters appear in a reference token.</i></p><p>Why these specific characters?</p><ul><li><code>/</code> separates tokens, so it must be escaped inside a token</li><li><code>~</code> is the escape character itself, so it must also be escaped</li></ul><p>The escape sequences are:</p><ul><li><code>~0</code> represents <code>~</code> (tilde)</li><li><code>~1</code> represents <code>/</code> (forward slash)</li></ul><h3 id="the-library-handles-escaping-automatically"><a href="#the-library-handles-escaping-automatically" class="anchor"></a>The Library Handles Escaping Automatically</h3><p><b>Important</b>: When using <code>json-pointer</code> programmatically, you rarely need to think about escaping. The <code>Mem</code> variant stores unescaped strings, and escaping happens automatically during serialization:</p><pre class="language-ocaml"><code># let p = make [mem "a/b"];;
126126val p : nav t = [Mem "a/b"]
127127# to_string p;;
128128- : string = "/a~1b"
···137137- : string = "~0~1"</code></pre><h3 id="unescaping"><a href="#unescaping" class="anchor"></a>Unescaping</h3><p>And the reverse process:</p><pre class="language-ocaml"><code># Token.unescape "a~1b";;
138138- : string = "a/b"
139139# Token.unescape "a~0b";;
140140-- : string = "a~b"</code></pre><h3 id="the-order-matters!"><a href="#the-order-matters!" class="anchor"></a>The Order Matters!</h3><p>RFC 6901, Section 4 is careful to specify the unescaping order:</p><p><i>Evaluation of each reference token begins by decoding any escaped character sequence. This is performed by first transforming any occurrence of the sequence '~1' to '/', and then transforming any occurrence of the sequence '~0' to '~'. By performing the substitutions in this order, an implementation avoids the error of turning '~01' first into '~1' and then into '/', which would be incorrect (the string '~01' correctly becomes '~1' after transformation).</i></p><p>Let's verify this tricky case:</p><pre class="language-ocaml"><code># Token.unescape "~01";;
141141-- : string = "~1"</code></pre><p>If we unescaped <code>~0</code> first, <code>~01</code> would become <code>~1</code>, which would then become <code>/</code>. But that's wrong! The sequence <code>~01</code> should become the literal string <code>~1</code> (a tilde followed by the digit one).</p><h2 id="uri-fragment-encoding"><a href="#uri-fragment-encoding" class="anchor"></a>URI Fragment Encoding</h2><p>JSON Pointers can be embedded in URIs. RFC 6901, Section 6 explains:</p><p><i>A JSON Pointer can be represented in a URI fragment identifier by encoding it into octets using UTF-8, while percent-encoding those characters not allowed by the fragment rule in RFC 3986.</i></p><p>This adds percent-encoding on top of the <code>~0</code>/<code>~1</code> escaping:</p><pre class="language-ocaml"><code># to_uri_fragment (of_string_nav "/foo");;
140140+- : string = "a~b"</code></pre><h3 id="the-order-matters!"><a href="#the-order-matters!" class="anchor"></a>The Order Matters!</h3><p><a href="https://datatracker.ietf.org/doc/html/rfc6901#section-4">RFC 6901, Section 4</a> is careful to specify the unescaping order:</p><p><i>Evaluation of each reference token begins by decoding any escaped character sequence. This is performed by first transforming any occurrence of the sequence '~1' to '/', and then transforming any occurrence of the sequence '~0' to '~'. By performing the substitutions in this order, an implementation avoids the error of turning '~01' first into '~1' and then into '/', which would be incorrect (the string '~01' correctly becomes '~1' after transformation).</i></p><p>Let's verify this tricky case:</p><pre class="language-ocaml"><code># Token.unescape "~01";;
141141+- : string = "~1"</code></pre><p>If we unescaped <code>~0</code> first, <code>~01</code> would become <code>~1</code>, which would then become <code>/</code>. But that's wrong! The sequence <code>~01</code> should become the literal string <code>~1</code> (a tilde followed by the digit one).</p><h2 id="uri-fragment-encoding"><a href="#uri-fragment-encoding" class="anchor"></a>URI Fragment Encoding</h2><p>JSON Pointers can be embedded in URIs. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-6">RFC 6901, Section 6</a> explains:</p><p><i>A JSON Pointer can be represented in a URI fragment identifier by encoding it into octets using UTF-8, while percent-encoding those characters not allowed by the fragment rule in <a href="https://datatracker.ietf.org/doc/html/rfc3986">RFC 3986</a>.</i></p><p>This adds percent-encoding on top of the <code>~0</code>/<code>~1</code> escaping:</p><pre class="language-ocaml"><code># to_uri_fragment (of_string_nav "/foo");;
142142- : string = "/foo"
143143# to_uri_fragment (of_string_nav "/a~1b");;
144144- : string = "/a~1b"
···226226# set (of_string "/tasks/0") tasks ~value:(Jsont.Json.string "buy eggs");;
227227- : Jsont.json = {"tasks":["buy eggs"]}
228228# set (of_string "/tasks/-") tasks ~value:(Jsont.Json.string "call mom");;
229229-- : Jsont.json = {"tasks":["buy milk","call mom"]}</code></pre><p>This is useful for implementing JSON Patch (<code>RFC 6902</code>) where operations like <code>"add"</code> can target either existing positions or the append marker. If you need to distinguish between pointer types at runtime, use <code>of_string_kind</code> which returns a polymorphic variant:</p><pre class="language-ocaml"><code># of_string_kind "/tasks/0";;
229229+- : Jsont.json = {"tasks":["buy milk","call mom"]}</code></pre><p>This is useful for implementing JSON Patch (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>) where operations like <code>"add"</code> can target either existing positions or the append marker. If you need to distinguish between pointer types at runtime, use <code>of_string_kind</code> which returns a polymorphic variant:</p><pre class="language-ocaml"><code># of_string_kind "/tasks/0";;
230230- : [ `Append of append t | `Nav of nav t ] = `Nav [Mem "tasks"; Nth 0]
231231# of_string_kind "/tasks/-";;
232232-- : [ `Append of append t | `Nav of nav t ] = `Append [Mem "tasks"] /-</code></pre><h2 id="summary"><a href="#summary" class="anchor"></a>Summary</h2><p>JSON Pointer (RFC 6901) provides a simple but powerful way to address values within JSON documents:</p><ol><li><b>Syntax</b>: Pointers are strings of <code>/</code>-separated reference tokens</li><li><b>Escaping</b>: Use <code>~0</code> for <code>~</code> and <code>~1</code> for <code>/</code> in tokens (handled automatically by the library)</li><li><b>Evaluation</b>: Tokens navigate through objects (by key) and arrays (by index)</li><li><b>URI Encoding</b>: Pointers can be percent-encoded for use in URIs</li><li><b>Mutations</b>: Combined with JSON Patch (RFC 6902), pointers enable structured updates</li><li><b>Type Safety</b>: Phantom types (<code>nav t</code> vs <code>append t</code>) prevent misuse of append pointers with retrieval operations, while the <code>any</code> existential type allows ergonomic use with mutation operations</li></ol><p>The <code>json-pointer</code> library implements all of this with type-safe OCaml interfaces, integration with the <code>jsont</code> codec system, and proper error handling for malformed pointers and missing values.</p><h3 id="key-points-on-json-pointer-vs-json-path"><a href="#key-points-on-json-pointer-vs-json-path" class="anchor"></a>Key Points on JSON Pointer vs JSON Path</h3><ul><li><b>JSON Pointer</b> addresses a <em>single</em> location (like a file path)</li><li><b>JSON Path</b> queries for <em>multiple</em> values (like a search)</li><li>The <code>-</code> token is unique to JSON Pointer - it means "append position" for arrays</li><li>The library uses phantom types to enforce that <code>-</code> (append) pointers cannot be used with <code>get</code>/<code>find</code></li></ul></div></body></html>
232232+- : [ `Append of append t | `Nav of nav t ] = `Append [Mem "tasks"] /-</code></pre><h2 id="summary"><a href="#summary" class="anchor"></a>Summary</h2><p>JSON Pointer (<a href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a>) provides a simple but powerful way to address values within JSON documents:</p><ol><li><b>Syntax</b>: Pointers are strings of <code>/</code>-separated reference tokens</li><li><b>Escaping</b>: Use <code>~0</code> for <code>~</code> and <code>~1</code> for <code>/</code> in tokens (handled automatically by the library)</li><li><b>Evaluation</b>: Tokens navigate through objects (by key) and arrays (by index)</li><li><b>URI Encoding</b>: Pointers can be percent-encoded for use in URIs</li><li><b>Mutations</b>: Combined with JSON Patch (<a href="https://datatracker.ietf.org/doc/html/rfc6902">RFC 6902</a>), pointers enable structured updates</li><li><b>Type Safety</b>: Phantom types (<code>nav t</code> vs <code>append t</code>) prevent misuse of append pointers with retrieval operations, while the <code>any</code> existential type allows ergonomic use with mutation operations</li></ol><p>The <code>json-pointer</code> library implements all of this with type-safe OCaml interfaces, integration with the <code>jsont</code> codec system, and proper error handling for malformed pointers and missing values.</p><h3 id="key-points-on-json-pointer-vs-json-path"><a href="#key-points-on-json-pointer-vs-json-path" class="anchor"></a>Key Points on JSON Pointer vs JSON Path</h3><ul><li><b>JSON Pointer</b> addresses a <em>single</em> location (like a file path)</li><li><b>JSON Path</b> queries for <em>multiple</em> values (like a search)</li><li>The <code>-</code> token is unique to JSON Pointer - it means "append position" for arrays</li><li>The library uses phantom types to enforce that <code>-</code> (append) pointers cannot be used with <code>get</code>/<code>find</code></li></ul></div></body></html>