JSON Pointer is a syntax that allows you to isolate a single element within a JSON document by navigating down a series of object properties and array indices.
Syntax
The syntax is really simple:
1
/objects/and/3/arrays
This pointer has four segments. Each segment specifies either an object property or, if the segment is a number, an array index. Interestingly, the 3 above could be either an object property or an array index. There’s nothing about the pointer that specifies a distinction. It will resolve for both of these documents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"objects": {
"and": [
"item zero",
null,
2,
{
"arrays": "found me"
}
]
}
}
{
"objects": {
"and": {
"3": {
"arrays": "found me"
}
}
}
}
If a property contains a /, it must be escaped by replacing it with ~1. Additionally, the escape character ~ must be escaped by replacing it with ~0.
It also supports a URL format, which is essentially the same thing, except that it starts with a #, then followed by the standard pointer. This format also will %-encode any URL-reserved characters, like = and ?.
In code
The JsonPointer class is the model for JSON Pointer.
There are three ways create pointers:
- parsing with either
Parse()orTryParse()1
var pointer = JsonPointer.Parse("/objects/and/3/arrays");
- building with
Create()and supplying the segments explicitly1
var pointer = JsonPointer.Create("object", "and", 3, "arrays");
Both of these options will give you an instance of the model that can be used to evaluate JSON data.
1
2
3
using var element = JsonDocument.Parse("{\"objects\":{\"and\":[\"item zero\",null,2,{\"arrays\":\"found me\"}]}}");
var result = pointer.Evaluate(element);
// result contains a JsonElement with a "found me" value
or
1
2
3
4
var element = JsonNode.Parse("{\"objects\":{\"and\":[\"item zero\",null,2,{\"arrays\":\"found me\"}]}}");
var success = pointer.TryEvaluate(element, out var result);
// success is true
// result contains a JsonNode with a "found me" value
The designers of the
JsonNodeAPI have elected (for reasons I disagree with) to consider JSON null and .Net null to be equivalent. This goes against both my personal experience building Manatee.Json and theJsonElementAPI, both of which maintain a separation between these two null concepts. Because of the unified design, it’s impossible to determine whether a returnedJsonNodevalue ofnullrepresents a value that is present but null or it is merely absent from the data. To accommodate this, the evaluation method can only support the familiarTryParse()signature. A return oftrueindicates the value was found, andfalseindicates it was not. In the case of atruereturn,resultmay still be null, indicating the value was found and was a JSON null.
Pointer math
You can also combine and augment pointers in different ways.
Joining two pointers together:
1
2
3
var pointer1 = JsonPointer.Parse("/objects/and");
var pointer2 = JsonPointer.Parse("/3/arrays");
var final = pointer1.Combine(pointer2);
Appending additional segments to an existing pointer:
1
2
var pointer = JsonPointer.Parse("/objects/and");
var final = pointer1.Combine(3, "arrays");
Access pointer parts and create sub-pointers
You can retrieve the individual segments using the indexer:
1
2
var pointer = JsonPointer.Parse("/objects/and/3/arrays");
var andSegment = pointer[1]; // "and" (string)
If you’re using .Net 8 or higher, the indexer also supports Range values, so you can obtain a new pointer containing a portion of the segments.
Get the immediate parent:
1
2
var pointer = JsonPointer.Parse("/objects/and/3/arrays");
var parent = pointer.GetParent(); // /objects/and/3
Or get the local pointer (imagine you’ve navigated to /objects/and/ and you need the pointer relative to where you are):
1
2
var pointer = JsonPointer.Parse("/objects/and/3/arrays");
var local = pointer.GetLocal(2); // /3/arrays
Relative JSON Pointers
JSON Hyperschema relies on a variation of JSON Pointers called Relative JSON Pointers that also includes the number of parent and/or array-index navigations. This allows the system to start at an internal node in the JSON document and navigate to another node potentially on another subtree.
Relative JSON Pointers are implemented with the RelativeJsonPointer struct. Interactions with this struct are very similar to JsonPointer.
Since evaluation of these pointers require parent navigation, a feature which is unsupported by JsonElements, only the JsonNodes can be processed.
Ahead of Time (AOT) compatibility
JsonPointer.Net v4 includes updates to support Native AOT applications. However because everything in this library is handled via parsing and direct-to-string output, you don’t need to do anything.
You’re done. Congratulations.