Skip to content
ostrovok-techPublic

About

JSONPath Erlang implementation

Resources

Stars

27 stars

Watchers

24 watching

Forks

Latest commit

 

History

43 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

eJSONPath - jsonpath for erlang

eJSONPath is pure-erlang implementation of JSONPath. It supports both map() and jiffy JSON structure ({[ {key(), value()}, ...]} for kv-objects) and implements most of the JSONPath description (I don't say specification, because there is no such thing like jsonpath spec).

  • Robust extensible parser (leex + yecc).
  • Extensible by custom functions.
  • No dependencies (but you may want jiffy or any other json parser).

Examples

{ok, Bin} = file:read_file("test/doc.json").
Doc = jsx:decode(Bin, [return_maps]).

%% return 1'st book author
{[<<"Nigel Rees">>], ["$['store']['book'][0]['author']"]} = ejsonpath:q("$.store.book[0].author", Doc).

%% return 1'st book categody and author
{[<<"reference">>,<<"Nigel Rees">>],
 ["$['store']['book'][0]['category']",
  "$['store']['book'][0]['author']"]} = ejsonpath:q("$.store.book[0]['category','author']", Doc).

%% return only reference book authors
%% `Funs' is a map or propist of `Name` and `Fun` pairs (see Fun spec in the sources)
Funs = #{
 filter_category =>
  fun({Map, _Doc}, [CategoryName]) ->
     case maps:find(<<"category">>, Map) of
         {ok, CategoryName} -> true;
         _ -> false
     end
  end
},
{[<<"Nigel Rees">>],["$['store']['book'][0]['author']"]} = ejsonpath:q("$.store.book[?(filter_category('reference'))].author", Doc, Funs).

%% update only item id == 0
O = #{
  <<"items">> => [
    #{<<"id">> => 0, <<"value">> => yyy},
    #{<<"id">> => 1, <<"value">> => yyy}
  ]
},

ejsonpath:tr("$.items[?(@.id == 0)].value", O, fun(_) -> {ok, xxx} end).
{
  #{<<"items">> =>
       [#{<<"id">> => 0,<<"value">> => xxx},
        #{<<"id">> => 1,<<"value">> => yyy}]},
  ["$['items'][0]['value']"]
 

% delete item if query match
Opts = [].
ejsonpath:tr("$.items[?(@.id == 0)]", O, fun (_) -> delete end, Funcs, Opts).
{ 
  #{<<"items">> => [#{<<"id">> => 1,<<"value">> => yyy}]},
  ["$['items'][0]"]
}

% delete item if query not matched
ejsonpath:tr("$.key", O, fun (_) -> delete end, Funcs, [handle_not_found]).
{ 
  #{<<"items">> => [#{<<"id">> => 1,<<"value">> => yyy}]},
  ["$['items'][0]"]
}

% create element (query not matched)
MatchFun = fun ({not_found, _Path, Key, #{node := OldMap}}) -> {ok, maps:put(Key, xxx, OldMap)} end.
ejsonpath:tr("$.key", O, MatchFun, Funcs, [handle_not_found]).
% result is the new document modified and the "jsonpath"
{#{<<"items">> =>
       [#{<<"id">> => 0,<<"value">> => yyy},
        #{<<"id">> => 1,<<"value">> => yyy}],
   <<"key">> => xxx},
 ["$['key']"]}

More examples in tests.

JSONPath coverage

Since there is no such thing as JSONPath specification, every implementer create it's own variations. But I try to follow description from JSONPath as close as possible.

+-----------------------+------------------------+-----------+
| Feature               | Example                |Implemented|
+-----------------------+------------------------+-----------+
|Dot filtering          |`$.one.two`             | Y         |
+-----------------------+------------------------+-----------+
|Brace filtering        |`$['one']['two']`       | Y         |
+-----------------------+------------------------+-----------+
|Array slicing          | `$[1,2,3]` `$.o[2]`    | Y         |
+-----------------------+------------------------+-----------+
|Hash slicing           | `$['one', 'two']`      | Y         |
+-----------------------+------------------------+-----------+
|Asterisk (hash, array) | `$.one.*` `$.one[*]`   | Y         |
+-----------------------+------------------------+-----------+
|Python-slicing         | `$[1:-1:2]`            | Partial*  |
+-----------------------+------------------------+-----------+
|Eval binary filter     | `$[?(true)]`           | Partial** |
|                       | `$[?(@.cat != 'mew')]` |           |
+-----------------------+------------------------+-----------+
|Eval index             | `$[('one')]`           | Partial** |
+-----------------------+------------------------+-----------+
|Recursive descent      | `..`                   | Y         |
+-----------------------+------------------------+-----------+

* Only step=1 supported now
** Limited scripting language: string, integer, binary operators and function calls

Most of the missing features can be implemented as custom functions

$.one[?( custom_function_call('arg1', 42) )]
$.two[( custom_function_call() )]

TODO

  • Implement missing features
  • Python slicing step support. (Currently only step==1 supported)
  • Eval filter / index - allow path expressions and operators $[?(@.category)]

Other implementations

License

Apache v2

About

JSONPath Erlang implementation

Resources

Stars

27 stars

Watchers

24 watching

Forks

Releases

Packages

Used by

Contributors

Languages