ExJSONPointer (ex_json_pointer v0.1.0)

View Source

An 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")
nil

iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "/a/b/c/4")
nil

iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => "hello"}}}, "#/a/b/d")
nil

iex> ExJSONPointer.resolve(%{"a" => %{"b" => %{"c" => [1, 2, 3]}}}, "#/a/b/c/4")
nil

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///")
nil

Invalid JSON pointer syntax:

iex> ExJSONPointer.resolve(%{"a" =>%{"b" => %{"c" => [1, 2, 3]}}}, "a/b")
{:error,
  "invalid JSON pointer syntax that not represented starts with `#` or `/`"}

iex> ExJSONPointer.resolve(%{"a" =>%{"b" => %{"c" => [1, 2, 3]}}}, "##/a")
{:error,
  "invalid URI fragment identifier"}

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

document()

@type document() :: map()

The JSON document to be processed, must be a map.

pointer()

@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 '#').

result()

@type result() :: nil | term() | {:error, String.t()}

The result of resolving a JSON Pointer:

  • nil - when the pointer does not resolve to a value
  • term() - the resolved value
  • {:error, String.t()} - when there is an error in pointer syntax

Functions

resolve(document, pointer)

@spec resolve(document(), pointer()) :: result()

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"