View Source JsonPtr (json_ptr v1.2.0)

Implementation of JSONPointer.

This module handles JSONPointers as an internal term representation and provides functions to manipulate the JSONPointer term and to use the representation to traverse or manipulate JSON data.

See: https://www.rfc-editor.org/rfc/rfc6901 for the specification.

Warning

Do not rely on the private internal implementation of JSONPointer, it may change in the future.

Link to this section Summary

Functions

rolls back the JsonPtr to the parent of its most distant leaf.

like backtrack/1, but raises if attempted to backtrack from the root.

Performs a each operation on the JSON data at the given pointer, analogous to Enum.each/2. Returns :ok when all iterations are complete

Performs a flat_map operation on the JSON data at the given pointer, analogous to Enum.flat_map/2.

converts a path to a JsonPtr

converts a URI (or a URI-string) to a JsonPtr.

appends path to the JsonPtr. This may either be a t:String.t, a list of t:String.t.

Performs a map operation on the JSON data at the given pointer, analogous to Enum.map/2.

returns the last part of the pointer and the pointer without it.

Performs a reduction operation on the JSON data at the given pointer, analogous to Enum.reduce/3.

given some JSON data, resolves the content pointed to by the JsonPtr.

given some JSON data, resolves the content pointed to by the JsonPtr.

creates a JsonPtr to its path equivalent.

creates a URI.t/0 struct out of a JsonPtr.

updates nested JSON data at the location given by the JsonPtr.

Link to this section Types

@type json() ::
  nil
  | boolean()
  | String.t()
  | number()
  | [json()]
  | %{optional(String.t()) => json()}
@opaque t()

Link to this section Functions

@spec backtrack(t()) :: {:ok, t()} | :error

rolls back the JsonPtr to the parent of its most distant leaf.

iex> {:ok, ptr} = "/foo/bar" |> JsonPtr.from_path |> JsonPtr.backtrack
iex> JsonPtr.to_path(ptr)
"/foo"
@spec backtrack!(t()) :: t()

like backtrack/1, but raises if attempted to backtrack from the root.

Link to this function

each(pointer, data, fun)

View Source
@spec each(t(), json(), (t(), json() -> any())) :: :ok
@spec each(t(), json(), (t(), pos_integer() | String.t(), json() -> any())) :: :ok

Performs a each operation on the JSON data at the given pointer, analogous to Enum.each/2. Returns :ok when all iterations are complete

The iterator function will be passed the updated pointer and the data at that pointer.

If you pass an arity 3 function, it will also pass the key (or index) of the data in addition to the JsonPtr.

iex> ptr = JsonPtr.from_path("/foo")
iex> JsonPtr.each(ptr, %{"foo" => ["bar", "baz"]}, &send(self(), {JsonPtr.to_path(&1), &2}))
:ok
iex> receive do data -> data end
{"/foo/0", "bar"}
iex> receive do data -> data end
{"/foo/1", "baz"}
iex> JsonPtr.each(ptr, %{"foo" => ["bar", "baz"]}, &send(self(), {JsonPtr.to_path(&1), &2, &3}))
:ok
iex> receive do data -> data end
{"/foo/0", 0, "bar"}
iex> receive do data -> data end
{"/foo/1", 1, "baz"}
Link to this function

flat_map(pointer, data, fun)

View Source
@spec flat_map(t(), json(), (t(), json() -> [result])) :: [result] when result: term()
@spec flat_map(t(), json(), (t(), pos_integer() | String.t(), json() -> [result])) ::
  [result]
when result: term()

Performs a flat_map operation on the JSON data at the given pointer, analogous to Enum.flat_map/2.

If you pass an arity 3 function, it will also pass the key (or index) of the data in addition to the JsonPtr.

The iterator function will be passed the updated pointer and the data at that pointer.

iex> ptr = JsonPtr.from_path("/foo")
iex> JsonPtr.flat_map(ptr, %{"foo" => %{"bar" => "baz"}}, fn ptr, data -> [{JsonPtr.to_path(ptr), data}] end)
[{"/foo/bar", "baz"}]
iex> JsonPtr.flat_map(ptr, %{"foo" => ["bar", "baz"]}, fn ptr, data -> [{JsonPtr.to_path(ptr), data}] end)
[{"/foo/0", "bar"}, {"/foo/1", "baz"}]
@spec from_path(Path.t()) :: t()

converts a path to a JsonPtr

iex> JsonPtr.from_path("/") # the root-only case
[]
iex> JsonPtr.from_path("/foo/bar")
["foo", "bar"]
iex> JsonPtr.from_path("/foo~0bar/baz")
["foo~bar", "baz"]
iex> JsonPtr.from_path("/currency/%E2%82%AC")
["currency", "€"]
@spec from_uri(URI.t() | String.t()) :: t()

converts a URI (or a URI-string) to a JsonPtr.

iex> JsonPtr.from_uri("#/foo/bar")
["foo", "bar"]
iex> JsonPtr.from_uri("/foo/bar")
["foo", "bar"]
iex> JsonPtr.from_uri(%URI{path: "/foo/bar"})
["foo", "bar"]
iex> JsonPtr.from_uri(%URI{fragment: "/foo/bar", host: "elixir-lang.org"})
["foo", "bar"]
Link to this function

join(pointer, next_path)

View Source
@spec join(t(), String.t() | [String.t()]) :: t()

appends path to the JsonPtr. This may either be a t:String.t, a list of t:String.t.

iex> ptr = JsonPtr.from_path("/foo/bar")
iex> ptr |> JsonPtr.join("baz") |> JsonPtr.to_path
"/foo/bar/baz"
iex> ptr |> JsonPtr.join(["baz", "quux"]) |> JsonPtr.to_path
"/foo/bar/baz/quux"
@spec map(t(), json(), (t(), json() -> result)) :: [result] when result: term()
@spec map(t(), json(), (t(), pos_integer() | String.t(), json() -> result)) :: [
  result
]
when result: term()

Performs a map operation on the JSON data at the given pointer, analogous to Enum.map/2.

The iterator function will be passed the updated pointer and the data at that pointer.

If you pass an arity 3 function, it will also pass the key (or index) of the data in addition to the JsonPtr.

iex> ptr = JsonPtr.from_path("/foo")
iex> JsonPtr.map(ptr, %{"foo" => %{"bar" => "baz"}}, &{JsonPtr.to_path(&1), &2})
[{"/foo/bar", "baz"}]
iex> JsonPtr.map(ptr, %{"foo" => ["bar", "baz"]}, &{JsonPtr.to_path(&1), &2})
[{"/foo/0", "bar"}, {"/foo/1", "baz"}]
iex> JsonPtr.map(ptr, %{"foo" => %{"bar" => "baz"}}, &{JsonPtr.to_path(&1), &2, &3})
[{"/foo/bar", "bar", "baz"}]
iex> JsonPtr.map(ptr, %{"foo" => ["bar", "baz"]}, &{JsonPtr.to_path(&1), &2, &3})
[{"/foo/0", 0, "bar"}, {"/foo/1", 1, "baz"}]
@spec pop(t()) :: {t(), String.t()} | :error

returns the last part of the pointer and the pointer without it.

iex> {rest, last} = "/foo/bar" |> JsonPtr.from_path |> JsonPtr.pop
iex> last
"bar"
iex> JsonPtr.to_path(rest)
"/foo"
iex> "/" |> JsonPtr.from_path |> JsonPtr.pop
:error
Link to this function

reduce(pointer, data, acc, fun)

View Source
@spec reduce(t(), json(), acc, (t(), json(), acc -> acc)) :: acc when acc: term()
@spec reduce(t(), json(), acc, (t(), pos_integer() | String.t(), json(), acc -> acc)) ::
  acc
when acc: term()

Performs a reduction operation on the JSON data at the given pointer, analogous to Enum.reduce/3.

The iterator function will be passed the updated pointer, the data and the accumulator at that pointer.

If you pass an arity 4 function, it will also pass the key (or index) of the data in addition to the JsonPtr.

iex> ptr = JsonPtr.from_path("/foo")
iex> JsonPtr.reduce(ptr, %{"foo" => %{"bar" => "baz"}}, %{}, &Map.put(&3, JsonPtr.to_path(&1), &2))
%{"/foo/bar" => "baz"}
iex> JsonPtr.reduce(ptr, %{"foo" => ["bar", "baz"]}, %{}, &Map.put(&3, JsonPtr.to_path(&1), &2))
%{"/foo/0" => "bar", "/foo/1" => "baz"}
iex> JsonPtr.reduce(ptr, %{"foo" => %{"bar" => "baz"}}, %{}, &Map.put(&4, {JsonPtr.to_path(&1), &2}, &3))
%{{"/foo/bar", "bar"} => "baz"}
iex> JsonPtr.reduce(ptr, %{"foo" => ["bar", "baz"]}, %{}, &Map.put(&4, {JsonPtr.to_path(&1), &2}, &3))
%{{"/foo/0", 0} => "bar", {"/foo/1", 1}=> "baz"}
Link to this function

resolve_json(data, pointer)

View Source
@spec resolve_json(data :: json(), t() | String.t()) ::
  {:ok, json()} | {:error, String.t()}

given some JSON data, resolves the content pointed to by the JsonPtr.

Note

the json pointer is the second parameter to this function.

iex> JsonPtr.resolve_json(true, "/")
{:ok, true}
iex> JsonPtr.resolve_json(%{"foo~bar" => "baz"}, "/foo~0bar")
{:ok, "baz"}
iex> JsonPtr.resolve_json(%{"€" => ["quux", "ren"]}, JsonPtr.from_path("/%E2%82%AC/1"))
{:ok, "ren"}
Link to this function

resolve_json!(data, pointer)

View Source
@spec resolve_json!(data :: json(), t() | String.t()) :: json()

given some JSON data, resolves the content pointed to by the JsonPtr.

Note

the json pointer is the second parameter to this function.

iex> JsonPtr.resolve_json!(true, "/")
true
iex> JsonPtr.resolve_json!(%{"foo~bar" => "baz"}, "/foo~0bar")
"baz"
iex> JsonPtr.resolve_json!(%{"€" => ["quux", "ren"]}, JsonPtr.from_path("/%E2%82%AC/1"))
"ren"
@spec to_path(t()) :: Path.t()

creates a JsonPtr to its path equivalent.

iex> JsonPtr.to_path(["foo", "bar"])
"/foo/bar"
iex> JsonPtr.to_path(["foo~bar", "baz"])
"/foo~0bar/baz"
iex> JsonPtr.to_path(["currency","€"])
"/currency/%E2%82%AC"
@spec to_uri(t()) :: URI.t()

creates a URI.t/0 struct out of a JsonPtr.

The JsonPtr is placed in the :fragment field of the URI.

iex> JsonPtr.to_uri(["foo", "bar"])
%URI{fragment: "/foo/bar"}
Link to this function

update_json!(object, list, transformation)

View Source
@spec update_json!(data :: json(), t(), (json() -> json())) :: json()

updates nested JSON data at the location given by the JsonPtr.

iex> ptr = JsonPtr.from_path("/foo/0")
iex> JsonPtr.update_json!(%{"foo" => [1, 2]}, ptr, &(&1 + 1))
%{"foo" => [2, 2]}
iex> JsonPtr.update_json!(%{"foo" => %{"0" => 1}}, ptr, &(&1 + 1))
%{"foo" => %{"0" => 2}}