philiprehberger-json_path
JSONPath expression evaluator with dot notation, wildcards, slices, filters, and recursive descent
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-json_path"
Or install directly:
gem install philiprehberger-json_path
Usage
require "philiprehberger/json_path"
data = {
'store' => {
'books' => [
{ 'title' => 'Ruby', 'price' => 30 },
{ 'title' => 'Python', 'price' => 25 },
{ 'title' => 'Go', 'price' => 20 }
]
}
}
Philiprehberger::JsonPath.query(data, '$.store.books[*].title')
# => ["Ruby", "Python", "Go"]
Philiprehberger::JsonPath.values(data, '$.store.books[*].title')
# => ["Ruby", "Python", "Go"] (alias for query)
Philiprehberger::JsonPath.first(data, '$.store.books[0].title')
# => "Ruby"
Philiprehberger::JsonPath.count(data, '$.store.books[*]')
# => 3
Philiprehberger::JsonPath.exists?(data, '$.store.books')
# => true
Matched paths
Use paths to get canonical JSONPath strings for every match, rather than the values themselves. Each returned path re-evaluates back to the single element it identifies, making it handy for diffing, patching, or reporting.
Philiprehberger::JsonPath.paths(data, '$.store.books[*].title')
# => ["$.store.books[0].title", "$.store.books[1].title", "$.store.books[2].title"]
Philiprehberger::JsonPath.paths(data, '$.store.books[?(@.price>22)]')
# => ["$.store.books[0]", "$.store.books[1]"]
Philiprehberger::JsonPath.paths(data, '$.missing')
# => []
Recursive Descent
Philiprehberger::JsonPath.query(data, '$..price')
# => [30, 25, 20]
Philiprehberger::JsonPath.query(data, '$..title')
# => ["Ruby", "Python", "Go"]
Array Indexing and Slicing
Philiprehberger::JsonPath.query(data, '$.store.books[0]')
# => [{"title"=>"Ruby", "price"=>30}]
Philiprehberger::JsonPath.query(data, '$.store.books[-1].title')
# => ["Go"]
Philiprehberger::JsonPath.query(data, '$.store.books[0:2].title')
# => ["Ruby", "Python"]
Filter Expressions
Philiprehberger::JsonPath.query(data, '$.store.books[?(@.price>22)].title')
# => ["Ruby", "Python"]
Philiprehberger::JsonPath.query(data, "$.store.books[?(@.title=='Go')].price")
# => [20]
Negation Filters
data = { 'items' => [{ 'name' => 'a', 'hidden' => true }, { 'name' => 'b' }] }
Philiprehberger::JsonPath.query(data, '$.items[?(!@.hidden)].name')
# => ["b"]
Length Comparisons
data = {
'groups' => [
{ 'name' => 'team1', 'members' => ['Alice', 'Bob'] },
{ 'name' => 'team2', 'members' => [] }
]
}
Philiprehberger::JsonPath.query(data, '$.groups[?(@.members.length > 0)].name')
# => ["team1"]
Supported Syntax
| Syntax | Description |
|---|---|
$ |
Root element |
.key |
Dot notation for object keys |
['key'] |
Bracket notation for object keys |
[n] |
Array index (supports negative) |
[*] |
Wildcard (all elements) |
[start:end] |
Array slice |
..key |
Recursive descent (match key at any depth) |
[?(@.key>val)] |
Filter expression |
[?(@.key)] |
Existence filter |
[?(!@.key)] |
Negation filter |
[?(@.key.length>n)] |
Length comparison filter |
API
| Method | Description |
|---|---|
JsonPath.query(data, path) |
Return all matches as an array |
JsonPath.values(data, path) |
Alias for query |
JsonPath.first(data, path) |
Return the first match or nil |
JsonPath.count(data, path) |
Return the number of matches |
JsonPath.exists?(data, path) |
Check if any match exists |
JsonPath.paths(data, path) |
Return canonical JSONPath strings for each match |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: