Warpath v0.4.1 Warpath View Source
A implementation of Jsonpath expression proposal by Stefan Goessner for Elixir.
Operators
| Operator | Description |
|---|---|
$ | The root element to query. This starts all path expressions. |
@ | The current node being processed by a filter predicate. |
* | Wildcard. All objects/elements regardless their names. |
.. | Deep scan, recursive descent. |
.name | Dot-notated child, it support string or atom as keys. |
['name'],["name"] | Bracket-notated child, it support string or atom as keys. |
[int (,int>)] | Array index or indexes |
[start:end:step] | Array slice operator. Start index inclusive, end index exclusive. |
[?(expression)] | Filter expression. Expression must evaluate to a boolean value. |
Filter operators
All filter operator supported by Warpath have the same behavior of Elixir lang, it means that it's possible to compare different data types, check the Elixir getting started page for more information about cross comparision on data types.
Filter are expression that must be result on a boolean value, Warpath will use then to retain data when filter a data structure;
a filter expression have it syntax like this [?( @.category == 'fiction' )].
| Operator | Description |
|---|---|
| == | left is equal to right |
| === | left is equal to right in strict mode |
| != | left is not equal to right |
| !== | left is not equal to right in strict mode |
| < | left is less than right |
| <= | left is less or equal to right |
| > | left is greater than right |
| >= | left is greater than or equal to right |
| in | left exists in right [?(@.price in [10, 20, 30])] |
| and,&& | logical and operator [?(@.price > 50 and @.price < 100)] |
| or,|| | logical or operator [?(@.category == 'fiction' or @.price < 100)] |
| not | logical not operator [?(not @.category == 'fiction')] |
Functions allowed in filter expression
| Function | Description |
|---|---|
| is_atom/1 | check if the given expression argument is evaluate to atom |
| is_binary/1 | check if the given expression argument is evaluate to binary |
| is_boolean/1 | check if the given expression argument is evaluate to boolean |
| is_float/1 | check if the given expression argument is evaluate to float |
| is_integer/1 | check if the given expression argument is evaluate to integer |
| is_list/1 | check if the given expression argument is evaluate to list |
| is_map/1 | check if the given expression argument is evaluate to map |
| is_nil/1 | check if the given expression argument is evaluate to nil |
| is_number/1 | check if the given expression argument is evaluate to number |
| is_tuple/1 | check if the given expression argument is evaluate to tuple |
Examples
All children
#wildcard using bracket-notation
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[*]")
{:ok, [100, 200, 300]}
#wildcard using dot-notation
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers.*")
{:ok, [100, 200, 300]}
Children lookup by name
#Simple string
iex>Warpath.query(%{"category" => "fiction", "price" => 12.99}, "$.category")
{:ok, "fiction"}
#Quoted string
iex>Warpath.query(%{"key with whitespace" => "some value"}, "$.['key with whitespace']")
{:ok, "some value"}
#Simple atom
iex>Warpath.query(%{atom_key: "some value"}, "$.:atom_key")
{:ok, "some value"}
#Quoted atom
iex>Warpath.query(%{"atom key": "some value"}, ~S{$.:'atom key'})
{:ok, "some value"}
#Unicode support
iex>Warpath.query(%{"🌢" => "Elixir"}, "$.🌢")
{:ok, "Elixir"}
#Union
iex>document = %{"key" => "value", "another" => "entry"}
...> Warpath.query(document, "$['key', 'another']")
{:ok, ["value", "entry"]}
Children lookup by index
#Positive index
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0]")
{:ok, 100}
#Negative index
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[-1]")
{:ok, 300}
#Union
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]")
{:ok, [100, 200]}
Slice
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[0:2:1]")
{:ok, [0, 1]}
#optional start and step param.
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[:2]")
{:ok, [0, 1]}
#Negative start index
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[-2:]")
{:ok, [3, 4]}
Filter
# Using logical and operator with is_integer function guard to gain strictness
iex>document = %{
...> "store" => %{
...> "car" => %{"price" => 100_000},
...> "bicycle" => %{"price" => 500}
...> }
...>}
...> Warpath.query(document, "$..*[?( @.price > 500 and is_integer(@.price) )]")
{:ok, [%{"price" => 100000}]}
# Deep path matching
iex> addresses = [
...> %{"address" => %{"state" => "Bahia"}},
...> %{"address" => %{"state" => "São Paulo"}}
...> ]
...> Warpath.query(addresses, "$[?(@.address.state=='Bahia')]")
{:ok,
[
%{
"address" => %{
"state" => "Bahia"
}
}
]}
#has children using named key
iex> document = %{
...> "store" => %{
...> "car" => %{"price" => 100_000},
...> "bicyle" => %{"price" => 500}
...> }
...> }
...> Warpath.query(document, "$..*[?(@.price)]")
{:ok, [%{"price" => 500}, %{"price" => 100_000}]}
#has children using index
iex> document = [ [1,2,3], [0,5], [], [1], 9, [9,8,7] ]
...> Warpath.query(document, "$[?( @[2] )]") # That means give me all list that have index 2.
{:ok, [ [1,2,3], [9,8,7]] }
Recursive descendant
#Collect key
iex>document = %{"store" => %{"car" => %{"price" => 100_000}, "bicycle" => %{"price" => 500}}}
...>Warpath.query(document, "$..price")
{:ok, [500, 100_000]}
#Collect index
iex> document = [ [1,2,3], [], :item, [0,5], [1], 9, [9,8,7] ]
...>Warpath.query(document, "$..[2]")
{:ok, [:item, 3, 7]}
#Using filter criteria to scan
iex> document = [ [1,2], [], :item, 9, [9,8], 1.1, "string" ]
...>Warpath.query(document, "$..[?( is_list(@) )]")
{:ok, [ [1, 2], [], [9, 8]]}
Options
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers")
{:ok, [100, 200, 300]}
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]", result_type: :path)
{:ok, ["$['integers'][0]", "$['integers'][1]"]}
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0]", result_type: :path_tokens)
{:ok, [{:root, "$"}, {:property, "integers"}, {:index_access, 0}]}
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]", result_type: :value_path)
{:ok, [{100, "$['integers'][0]"}, {200, "$['integers'][1]"}]}
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0]", result_type: :value_path_tokens)
{:ok, {100, [{:root, "$"}, {:property, "integers"}, {:index_access, 0}]}}
Link to this section Summary
Link to this section Types
Link to this type
opts()
View Sourceopts() :: [
{:result_type,
:value | :path | :value_path | :path_tokens | :value_path_tokens}
]
Link to this section Functions
Query data for the given expression.
Example
iex> data_structure = %{"name" => "Warpath"}
...> Warpath.query(data_structure, "$.name")
{:ok, "Warpath"}
iex> raw_json = ~s/{"name": "Warpath"}/
...> Warpath.query(raw_json, "$.name")
{:ok, "Warpath"}
iex> #Pass a compiled expression as selector
...> {:ok, expression} = Warpath.Expression.compile("$.autobots[0]")
...> Warpath.query(%{"autobots" => ["Optimus Prime", "Warpath"]}, expression)
{:ok, "Optimus Prime"}
Options:
result_type:
:value- return the value of evaluated expression -default:path- return the bracketfiy path string representation of evaluated expression instead of it's value:value_path- return both value and bracketify path string.:path_tokens- return the path tokens instead of it string representation, seeWarpath.Element.Path.:value_path_tokens- return both value and path tokens.
The same as query/3, but rise exception if it fail.