ExJSONPointer (ex_json_pointer v0.2.0)
View SourceAn Elixir implementation of RFC 6901 JSON Pointer for locating specific values within JSON documents.
Usage
The JSON pointer string syntax can be represented as a JSON string:
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "/a/b/c")
"hello"
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "/a/b")
%{"c" => "hello"}
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "/a/b/c")
[1, 2, 3]
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "/a/b/c/2")
3
iex> ExJSONPointer.resolve(%{"a" => [%{"b" => %{"c" => [1, 2]}}, 2, 3]}, "/a/2")
3
iex> ExJSONPointer.resolve(%{"a" => [%{"b" => %{"c" => [1, 2]}}, 2, 3]}, "/a/0/b/c/1")
2
or a URI fragment identifier:
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "#/a/b/c")
"hello"
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "#/a/b")
%{"c" => "hello"}
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "#/a/b/c")
[1, 2, 3]
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "#/a/b/c/2")
3
iex> ExJSONPointer.resolve(%{"a" => [%{"b" => %{"c" => [1, 2]}}, 2, 3]}, "#/a/2")
3
iex> ExJSONPointer.resolve(%{"a" => [%{"b" => %{"c" => [1, 2]}}, 2, 3]}, "#/a/0/b/c/1")
2
Some cases that a JSON pointer that references a nonexistent value:
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "/a/b/d")
{:error, "not found"}
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "/a/b/c/4")
{:error, "not found"}
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "#/a/b/d")
{:error, "not found"}
iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "#/a/b/c/4")
{:error, "not found"}
Some cases that a JSON pointer has some empty reference tokens, and link a $ref
test case from JSON Schema Test Suite(draft 2020-12) for reference.
iex> ExJSONPointer.resolve(%{"" => %{"" => 1}}, "/")
%{"" => 1}
iex> ExJSONPointer.resolve(%{"" => %{"" => 1}}, "//")
1
iex> ExJSONPointer.resolve(%{"" => %{"" => 1, "b" => %{"" => 2}}}, "//b")
%{"" => 2}
iex> ExJSONPointer.resolve(%{"" => %{"" => 1, "b" => %{"" => 2}}}, "//b/")
2
iex> ExJSONPointer.resolve(%{"" => %{"" => 1, "b" => %{"" => 2}}}, "//b///")
{:error, "not found"}
Invalid JSON pointer syntax:
iex> ExJSONPointer.resolve(%{"a" =>%{"b" => %{"c" => [1, 2, 3]}}}, "a/b")
{:error, "invalid pointer syntax"}
iex> ExJSONPointer.resolve(%{"a" =>%{"b" => %{"c" => [1, 2, 3]}}}, "##/a")
{:error, "invalid pointer syntax"}
Please see the test cases for more examples.
Summary
Types
The JSON document to be processed, must be a map.
The JSON Pointer string that follows RFC 6901 specification. Can be either a JSON String Representation (starting with '/') or a URI Fragment Identifier Representation (starting with '#').
The result of resolving a JSON Pointer
Functions
Resolve the JSON document with the given JSON Pointer to find the accompanying value.
Types
@type document() :: map()
The JSON document to be processed, must be a map.
@type pointer() :: String.t()
The JSON Pointer string that follows RFC 6901 specification. Can be either a JSON String Representation (starting with '/') or a URI Fragment Identifier Representation (starting with '#').
The result of resolving a JSON Pointer:
term()
- the resolved value{:error, String.t()}
- when there is an error in pointer syntax or value not found
Functions
Resolve the JSON document with the given JSON Pointer to find the accompanying value.
The pointer can be either:
- An empty string ("") or "#" to reference the whole document
- A JSON String Representation starting with "/"
- A URI Fragment Identifier Representation starting with "#"
Examples
iex> doc = %{"foo" => %{"bar" => "baz"}}
iex> ExJSONPointer.resolve(doc, "/foo/bar")
"baz"
iex> ExJSONPointer.resolve(doc, "/foo/baz")
{:error, "not found"}
iex> ExJSONPointer.resolve(doc, "##foo")
{:error, "invalid pointer syntax"}