@swaggerexpert/json-pointer
is a parser, validator, evaluator, compiler and representer for RFC 6901 JavaScript Object Notation (JSON) Pointer.
|
Get professionally supported @swaggerexpert/json-pointer with Tidelift Subscription. |
- Getting started
- More about JSON Pointer
- License
You can install @swaggerexpert/json-pointer
using npm
:
$ npm install @swaggerexpert/json-pointer
Include following script tag into your HTML file:
<script src="https://unpkg.com/@swaggerexpert/json-pointer@latest/dist/json-pointer.browser.min.js"></script>
Global variable JSONPointer
will be available in the browser matching interface of @swaggerexpert/json-pointer
npm package.
json-pointer.browser.min.js
is UMD minified build artifact.
There is also unminified json-pointer.browser.js
artifact, suitable for debugging.
Include following script tag into your HTML file (ESM mode):
<script type="module">
import * as JSONPointer from 'https://cdn.jsdelivr.net/npm/@swaggerexpert/json-pointer@latest/+esm'
</script>
Local variable JSONPointer
will be available in scope of the <script>
tag matching interface of @swaggerexpert/json-pointer
package.
Or include following script tag into your HTML file (UMD mode):
<script src="https://cdn.jsdelivr.net/npm/@swaggerexpert/json-pointer@latest/dist/json-pointer.browser.min.js"></script>
Global variable JSONPointer
will be available in the browser matching interface of @swaggerexpert/json-pointer
npm package.
json-pointer.browser.min.js
is UMD minified build artifact.
There is also unminified json-pointer.browser.js
artifact, suitable for debugging.
@swaggerexpert/json-pointer
currently supports parsing, validation ,evaluation, compilation and representation.
Both parser and validator are based on a superset of ABNF (SABNF)
and use apg-lite parser generator.
Parsing a JSON Pointer is as simple as importing the parse function and calling it.
import { parse } from '@swaggerexpert/json-parse';
const parseResult = parse('/foo/bar');
parseResult variable has the following shape:
{
result: <ParseResult['result]>,
tree: <ParseResult['tree']>,
stats: <ParseResult['stats']>,
trace: <ParseResult['trace']>,
}
TypeScript typings are available for all fields attached to parse result object returned by the parse
function.
@swaggerexpert/json-pointer
provides several translators to convert the parse result into different tree representations.
Concrete Syntax Tree (Parse tree) representation is available on parse result
when instance of CSTTranslator
is provided via a translator
option to the parse
function.
CST is suitable to be consumed by other tools like IDEs, editors, etc...
import { parse, CSTTranslator } from '@swaggerexpert/json-pointer';
const { tree: CST } = parse('/foo/bar', { translator: new CSTTranslator() });
CST tree has a shape documented by TypeScript typings (CSTTree).
Default translator. Abstract Syntax Tree representation is available on parse result
by default or when instance of ASTTranslator
is provided via a translator
option to the parse
function.
AST is suitable to be consumed by implementations that need to analyze the structure of the JSON Pointer
or for building a custom JSON Pointer evaluation engine.
AST of the parsed JSON Pointer is a list of unescaped reference tokens.
import { parse } from '@swaggerexpert/json-pointer';
const { tree: AST } = parse('/foo/bar'); // AST = ['foo', 'bar']
or
import { parse, ASTTranslator } from '@swaggerexpert/json-poiner';
const { tree: AST } = parse('/foo/bar', { translator: new ASTTranslator() }); // AST = ['foo', 'bar']
import { parse, XMLTranslator } from '@swaggerexpert/json-pointer';
const { tree: XML } = parse('$.store.book[0].title', { translator: new XMLTranslator() });
parse
function returns additional statistical information about the parsing process.
Collection of the statistics can be enabled by setting stats
option to true
.
import { parse } from '@swaggerexpert/json-pointer';
const { stats } = parse('/foo/bar', { stats: true });
stats.displayStats(); // returns operator stats
stats.displayHits(); // returns rules grouped by hit count
parse
function returns additional tracing information about the parsing process.
Tracing can be enabled by setting trace
option to true
. Tracing is essential
for debugging failed matches or analyzing rule execution flow.
import { parse } from '@swaggerexpert/json-pointer';
const { result, trace } = parse('1', { trace: true });
result.success; // returns false
trace.displayTrace(); // returns trace information
trace.inferExpectations(); // returns parser expectations
By combining information from result
and trace
, it is possible to analyze the parsing process in detail
and generate a messages like this: 'Invalid JSON Pointer: "1". Syntax error at position 0, expected "/"'
. Please see this
test file for more information how to achieve that.
Validating a JSON Pointer is as simple as importing one of the validation functions and calling it.
import {
testJSONPointer,
testReferenceToken,
testArrayLocation,
testArrayIndex,
testArrayDash
} from '@swaggerexpert/json-pointer';
testJSONPointer('/foo/bar'); // => true
testReferenceToken('foo'); // => true
testArrayLocation('0'); // => true
testArrayLocation('-'); // => true
testArrayIndex('0'); // => true
testArrayDash('-'); // => true
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.
import { escape } from '@swaggerexpert/json-pointer';
escape('~foo'); // => '~0foo'
escape('/foo'); // => '~1foo'
Unescape 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, this library avoids the error of
turning '~01'
first into '~1'
and then into '/'
, which would be
incorrect (the string '~01'
correctly becomes '~1'
after transformation).
import { unescape } from '@swaggerexpert/json-pointer';
unescape('~0foo'); // => '~foo'
unescape('~1foo'); // => '/foo'
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.
import { evaluate } from '@swaggerexpert/json-pointer';
const value = {
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
};
evaluate(value, ''); // => identical to value
evaluate(value, '/foo'); // => ["bar", "baz"]
evaluate(value, '/foo/0'); // => "bar"
evaluate(value, '/'); // => 0
evaluate(value, '/a~1b'); // => 1
evaluate(value, '/c%d'); // => 2
evaluate(value, '/e^f'); // => 3
evaluate(value, '/g|h'); // => 4
evaluate(value, '/i\\j'); // => 5
evaluate(value, '/k"l'); // => 6
evaluate(value, '/ '); // => 7
evaluate(value, '/m~0n'); // => 8
// neither object nor array
evaluate(null, '/foo'); // => throws JSONPointerTypeError
// arrays
evaluate(value, '/foo/2'); // => throws JSONPointerIndexError
evaluate(value, '/foo/-'); // => throws JSONPointerIndexError
evaluate(value, '/foo/a'); // => throws JSONPointerIndexError
// objects
evaluate(value, '/bar'); // => throws JSONPointerKeyError
By default, the evaluation is strict, meaning error condition will be raised if it fails to resolve a concrete value for any of the JSON pointer's reference tokens. For example, if an array is referenced with a non-numeric token, an error condition will be raised.
Note that the use of the "-"
character to index an array will always
result in such an error condition because by definition it refers to
a nonexistent array element.
This spec compliant strict behavior can be disabled by setting the strictArrays
option to false
.
evaluate(value, '/foo/2', { strictArrays: false }); // => undefined
evaluate(value, '/foo/-', { strictArrays: false }); // => undefined
evaluate(value, '/foo/a', { strictArrays: false }); // => undefined
By default, the evaluation is strict, meaning error condition will be raised if it fails to resolve a concrete value for any of the JSON pointer's reference tokens. For example, if a token references a key that is not present in an object, an error condition will be raised.
This spec compliant strict behavior can be disabled by setting the strictObjects
option to false
.
evaluate(value, '/bar', { strictObjects: false }); // => undefined
strictObjects
options has no effect in cases where evaluation of previous
reference token failed to resolve a concrete value.
evaluate(value, '/bar/baz', { strictObjects: false }); // => throw JSONPointerTypeError
An evaluation realm defines the rules for interpreting and navigating data structures in JSON Pointer evaluation.
While JSON Pointer traditionally operates on JSON objects and arrays, evaluation realms allow the evaluation to work
polymorphically with different data structures, such as Map,
Set, Immutable.js,
or even custom representations like ApiDOM.
Realm can be specified via the realm
option in the evalute()
function.
By default, the evaluation operates under the JSON realm, which assumes that:
- Arrays are indexed numerically.
- Objects (plain JavaScript objects) are accessed by string keys.
The default realm is represented by the JSONEvaluationRealm
class.
import { evaluate } from '@swaggerexpert/json-pointer';
evaluate({ a: 'b' }, '/a'); // => 'b'
is equivalent to:
import { evaluate } from '@swaggerexpert/json-pointer';
import JSONEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/json';
evaluate({ a: 'b' }, '/a', { realm: new JSONEvaluationRealm() }); // => 'b'
The Map/Set realm extends JSON Pointer evaluation to support Map and Set instances,
allowing structured traversal and access beyond traditional JavaScript objects and arrays.
Map/Set realm is represented by the MapSetEvaluationRealm
class.
import { evaluate } from '@swaggerexpert/json-pointer';
import MapSetEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/map-set';
const map = new Map([
['a', new Set(['b', 'c'])]
]);
evaluate(map, '/a/1', { realm: new MapSetEvaluationRealm() }); // => 'c'
The Minim Evaluation Realm extends JSON Pointer evaluation to support minim
data structures,
specifically ObjectElement
, ArrayElement
, and other element types from the minim.
Minim is widely used in API description languages (e.g., OpenAPI, API Blueprint, AsyncAPI and other API Description processing tools) to represent structured API data. The Minim Evaluation Realm enables seamless JSON Pointer traversal for these structures.
Before using the Minim Evaluation Realm, you need to install the minim
package:
$ npm install --save minim
import { ObjectElement } from 'minim';
import { evaluate } from '@swaggerexpert/json-pointer';
import MinimEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/minim';
const objectElement = new ObjectElement({
a: ['b', 'c']
});
evaluate(objectElement, '/a/1', { realm: new MinimEvaluationRealm() }); // => StringElement('c')
The ApiDOM Evaluation Realm is an integration layer that enables
evaluation of JSON Pointer expressions on ApiDOM structures. It provides compatibility with ApiDOM core and namespace packages (@swagger-api/apidom-ns-*
),
allowing to traverse and query ApiDOM element instances.
Before using the ApiDOM Evaluation Realm, you need to install the @swagger-api/apidom-core
package:
$ npm install --save @swagger-api/apidom-core
import { ObjectElement } from '@swagger-api/apidom-core';
import { evaluate } from '@swaggerexpert/json-pointer';
import ApiDOMEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/apidom';
const objectElement = new ObjectElement({
a: ['b', 'c']
});
evaluate(objectElement, '/a/1', { realm: new ApiDOMEvaluationRealm() }); // => StringElement('c')
The Immutable.js Evaluation Realm is an integration layer that enables evaluation of JSON Pointer expressions on Immutable.js structures.
Before using the Immutable.js Evaluation Realm, you need to install the immutable
package:
$ npm install --save immutable
import { fromJS } from 'immutable';
import { evaluate } from '@swaggerexpert/json-pointer';
import ImmutableEvaluationRealm from '@swaggerexpert/json-pointer/evaluate/realms/immutable';
const map = fromJS({
a: ['b', 'c']
});
evaluate(map, '/a/1', { realm: new ImmutableEvaluationRealm() }); // => 'c'
The evaluation is designed to support custom evaluation realms, enabling JSON Pointer evaluation for non-standard data structures.
A valid custom evaluation realm must match the structure of the EvaluationRealm interface.
One way to create a custom realm is to extend the EvaluationRealm
class and implement the required methods.
import { evaluate, EvaluationRealm } from '@swaggerexpert/json-pointer';
class CustomEvaluationRealm extends EvaluationRealm {
name = 'cusotm';
isArray(node) { ... }
isObject(node) { ... }
sizeOf(node) { ... }
has(node, referenceToken) { ... }
evaluate(node, referenceToken) { ... }
}
evaluate({ a: 'b' }, '/a', { realm: new CustomEvaluationRealm() }); // => 'b'
Evaluation realms can be composed to create complex evaluation scenarios, allowing JSON Pointer evaluation to work across multiple data structures in a seamless manner. By combining different realms, composite evaluation ensures that a JSON Pointer query can resolve correctly whether the data structure is an object, array, Map, Set, or any custom type.
When composing multiple evaluation realms, the order matters. The composition is performed from left to right, meaning:
- More specific realms should be placed first (leftmost position).
- More generic realms should be placed later (rightmost position).
This ensures that specialized data structures (e.g., Map, Set, Immutable.js) take precedence over generic JavaScript objects and arrays.
import { composeRealms, evaluate } from '@swaggerexpert/json-pointer';
import JSONEvaluationRealm from '@swaggerexpert/json-pointer/realms/json';
import MapSetEvaluationRealm from '@swaggerexpert/json-pointer/realms/map-set';
const compositeRealm = composeRealms(new MapSetEvaluationRealm(), new JSONEvaluationRealm());
const structure = [
{
a: new Map([
['b', new Set(['c', 'd'])]
]),
},
];
evaluate(structure, '/0/a/b/1', { realm : compositeRealm }); // => 'd'
@swaggerexpert/json-pointer
provides rich diagnostic information to help identify and resolve issues during JSON Pointer evaluation.
When evaluation fails, the library throws errors from a well-defined hierarchy — all extending from JSONPointerEvaluateError
.
These errors carry detailed diagnostic metadata describing what failed, where it failed, and why.
Each error includes following fields:
jsonPointer
– the full pointer being evaluatedreferenceToken
– the token that caused the failurereferenceTokenPosition
– the index of that token within the pointerreferenceTokens
– the full list of parsed reference tokenscurrentValue
– the value being evaluated at the point of failurerealm
– the name of the evaluation realm (e.g., "json")
@swaggerexpert/json-pointer
package supports evaluation tracing, allowing you to inspect each step of JSON Pointer evaluation in detail.
This is especially useful for debugging, error reporting, visualization tools, or implementing custom behavior like fallbacks and partial evaluations.
How it works?
To enable tracing, provide an empty trace object when calling evaluate.
trace
object is populated with detailed information about each step of the evaluation process:
Tracing successful
evaluation:
import { evaluate } from '@swaggerexpert/json-pointer';
const trace = {};
evaluate({ a: 'b' }, '/a', { trace });
// trace
{
steps: [
{
referenceToken: 'a',
referenceTokenPosition: 0,
input: { a: 'b' },
inputType: 'object',
output: 'b',
success: true
}
],
failed: false,
failedAt: -1,
message: 'JSON Pointer "/a" successfully evaluated against the provided value',
context: {
jsonPointer: '/a',
referenceTokens: [ 'a' ],
strictArrays: true,
strictObjects: true,
realm: 'json',
value: { a: 'b' }
}
}
Tracing failed
evaluation:
import { evaluate } from '@swaggerexpert/json-pointer';
const trace = {};
try {
evaluate({ a: 'b' }, '/c', { trace });
} catch {}
// trace
{
steps: [
{
referenceToken: 'c',
referenceTokenPosition: 0,
input: { a: 'b' },
inputType: 'object',
output: undefined,
success: false,
reason: 'Invalid object key "c" at position 0 in "/c": key not found in object'
}
],
failed: true,
failedAt: 0,
message: 'Invalid object key "c" at position 0 in "/c": key not found in object',
context: {
jsonPointer: '/c',
referenceTokens: [ 'c' ],
strictArrays: true,
strictObjects: true,
realm: 'json',
value: { a: 'b' }
}
}
Compilation is the process of transforming a list of unescaped reference tokens into a JSON Pointer. Reference tokens are escaped before compiled into a JSON Pointer.
import { compile } from '@swaggerexpert/json-pointer';
compile(['~foo', 'bar']); // => '/~0foo/bar'
A JSON Pointer can be represented in a JSON string value. Per
RFC4627, Section 2.5, all instances of quotation mark '"'
(%x22),
reverse solidus '\'
(%x5C), and control (%x00-1F) characters MUST be
escaped.
import { JSONString } from '@swaggerexpert/json-pointer';
JSONString.to('/foo"bar'); // => '"/foo\\"bar"'
JSONString.from('"/foo\\"bar"'); // => '/foo"bar'
A JSON Pointer can be represented in a URI fragment identifier by encoding it into octets using UTF-8 RFC3629, while percent-encoding those characters not allowed by the fragment rule in RFC3986.
import { URIFragmentIdentifier } from '@swaggerexpert/json-pointer';
URIFragmentIdentifier.to('/foo"bar'); // => '#/foo%22bar'
URIFragmentIdentifier.from('#/foo%22bar'); // => '/foo"bar'
@swaggerexpert/json-pointer
provides a structured error class hierarchy,
enabling precise error handling across JSON Pointer operations, including parsing, evaluation, compilation and validation.
import {
JSONPointerError,
JSONPointerParseError,
JSONPointerCompileError,
JSONPointerEvaluateError,
JSONPointerTypeError,
JSONPointerKeyError,
JSONPointerIndexError
} from '@swaggerexpert/json-pointer';
JSONPointerError is the base class for all JSON Pointer errors.
New grammar instance can be created in following way:
import { Grammar } from '@swaggerexpert/json-pointer';
const grammar = new Grammar();
To obtain original ABNF (SABNF) grammar as a string:
import { Grammar } from '@swaggerexpert/json-pointer';
const grammar = new Grammar();
grammar.toString();
// or
String(grammar);
JSON Pointer is defined by the following ABNF syntax
; JavaScript Object Notation (JSON) Pointer ABNF syntax
; https://datatracker.ietf.org/doc/html/rfc6901
json-pointer = *( slash reference-token ) ; MODIFICATION: surrogate text rule used
reference-token = *( unescaped / escaped )
unescaped = %x00-2E / %x30-7D / %x7F-10FFFF
; %x2F ('/') and %x7E ('~') are excluded from 'unescaped'
escaped = "~" ( "0" / "1" )
; representing '~' and '/', respectively
; https://datatracker.ietf.org/doc/html/rfc6901#section-4
array-location = array-index / array-dash
array-index = %x30 / ( %x31-39 *(%x30-39) )
; "0", or digits without a leading "0"
array-dash = "-"
; Surrogate named rules
slash = "/"
@swaggerexpert/json-pointer
is licensed under Apache 2.0 license.
@swaggerexpert/json-pointer
comes with an explicit NOTICE file
containing additional legal notices and information.