https://github.com/mtannaan/elixpath
JSONPath-like operations for Elixir's native data structure
https://github.com/mtannaan/elixpath
elixir elixpath jsonpath xpath
Last synced: about 1 month ago
JSON representation
JSONPath-like operations for Elixir's native data structure
- Host: GitHub
- URL: https://github.com/mtannaan/elixpath
- Owner: mtannaan
- License: mit
- Created: 2019-05-24T11:21:50.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2025-05-19T14:25:36.000Z (10 months ago)
- Last Synced: 2025-11-01T08:02:04.658Z (5 months ago)
- Topics: elixir, elixpath, jsonpath, xpath
- Language: Elixir
- Homepage:
- Size: 326 KB
- Stars: 12
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Elixpath
[](https://github.com/mtannaan/elixpath/actions/workflows/ci.yml)
[](https://codecov.io/gh/mtannaan/elixpath)
[](https://github.com/mtannaan/elixpath/blob/master/LICENSE)
[](https://hex.pm/packages/elixpath)
[](https://hexdocs.pm/elixpath/)
[](https://hex.pm/packages/elixpath)
Extract data from Elixir's native data structure using JSONPath-like path expressions.
## Searching for XPath Tools?
If you are planning to manipulate XML documents directly, other packages like [sweet_xml](https://hex.pm/packages/sweet_xml) can be better choices.
## Elixpath Expression
Elixpath's path expression is based on [JSONPath](https://goessner.net/articles/JsonPath/),
but mainly with following differences:
- Following Elixir's native expressions are supported:
- String, e.g. `..string."double-quoted string"`
- Atom, e.g. `.:atom.:"quoted atom"`
- Charlist, e.g. `.'single-quoted'`
- Integer, e.g. `[1][-1]`
- Several JSONPath features like following are not supported:
- "Current object" syntax element: `@`
- Union subscript operator: `[xxx,yyy]`
- Array slice operator: `[start:end:stop]`
- Filter and script expression using `()`
## Path Syntax
An Elixpath is represented by a sequence of following path components.
- `$` - root object. Optional. When present, this component has to be at the beginning of the path.
- `.(key expression)` or `[(key expression)]` - child objects that matches the given key.
- `..(key expression)` - descendant objects that matches the given key.
`(key expression)` above can be either of:
- _integer_ - used to specify index in lists. Value can be negative, e.g. `-1` represents the last element.
- _atom_ - starts with colon and can be quoted, e.g. `:atom`, `:"quoted_atom"`.
When `prefer_keys: :atom` option is given, preceding colon can be omitted.
- _string_ - double-quoted. Double quotation can be omitted unless `prefer_keys: :atom` option is given.
- _charlist_ - single-quoted.
- _wildcard_ - `*`. Represents all the children.
## Examples
```elixir
# string
iex> Elixpath.query(%{:a => 1, "b" => 2}, ~S/."b"/)
{:ok, [2]}
# you can use Elixpath.get! if you want only a single match
iex> Elixpath.get!(%{:a => 1, "b" => 2}, ".*")
1
# unquoted string
iex> Elixpath.query(%{:a => 1, "b" => 2}, ".b")
{:ok, [2]}
# no match
iex> Elixpath.query(%{:a => 1, "b" => 2}, ".nonsense")
{:ok, []}
# no match w/ get!
iex> Elixpath.get!(%{:a => 1, "b" => 2}, ".nonsense", _default = :some_default_value)
:some_default_value
# atom
iex> Elixpath.query(%{:a => 1, "b" => 2}, ".:a")
{:ok, [1]}
# integer
iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, ".:a[-1]")
{:ok, [%{c: 3}]}
# descendant
iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, "..:c")
{:ok, [3]}
# wildcard
iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, ".*.*.*")
{:ok, [2, 3]}
# enable sigil_p/2, which parses Elixpath at compile time.
iex> import Elixpath
iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, ~p".:a.1.:c")
{:ok, [3]}
# path syntax error for normal string is detected at runtime.
iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, ".:atom:syntax:error")
{:error, "expected member_expression while processing path"}
# while sigil_p raises a compilation error:
# iex> Elixpath.query(%{:a => [%{b: 2}, %{c: 3}]}, ~p".:atom:syntax:error")
# == Compilation error in file test/elixpath_test.exs ==
# ** (Elixpath.Parser.ParseError) ...
```