- Notifications
You must be signed in to change notification settings - Fork1
JSONPath, JSON Patch and JSON Pointer for Ruby
License
jg-rp/ruby-json-p3
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
We followRFC 9535 strictly and test against theJSONPath Compliance Test Suite.
Table of Contents
Add'json_p3' to your Gemfile:
gem 'json_p3', '~> 0.4.1'Or
gem install json_p3JSON P3 is cryptographically signed. To be sure the gem you install hasn't been tampered with, add my public key (if you haven't already) as a trusted certificate:
gem cert --add <(curl -Ls https://raw.githubusercontent.com/jg-rp/ruby-json-p3/refs/heads/main/certs/jgrp.pem)Followed by:
gem install json_p3 -P MediumSecurityJSON P3 has no runtime dependencies, so-P HighSecurity is OK too. Seehttps://guides.rubygems.org/security/ for more information.
require"json_p3"require"json"data=JSON.parse<<~JSON { "users": [ { "name": "Sue", "score": 100 }, { "name": "Sally", "score": 84, "admin": false }, { "name": "John", "score": 86, "admin": true }, { "name": "Jane", "score": 55 } ], "moderator": "John" }JSONJSONP3.find("$.users[?@.score > 85]",data).eachdo |node|putsnode.valueend# {"name"=>"Sue", "score"=>100}# {"name"=>"John", "score"=>86, "admin"=>true}
Or, reading JSON data from a file:
require"json_p3"require"json"data=JSON.load_file("/path/to/some.json")JSONP3.find("$.some.query",data).eachdo |node|putsnode.valueend
You could read data from a YAML formatted file too, or any data format that can be loaded into hashes and arrays.
require"json_p3"require"yaml"data=YAML.load_file("/tmp/some.yaml")JSONP3.find("$.users[?@.score > 85]",data).eachdo |node|putsnode.valueend
- Change log:https://github.com/jg-rp/ruby-json-p3/blob/main/CHANGELOG.md
- RubyGems:https://rubygems.org/gems/json_p3
- Source code:https://github.com/jg-rp/ruby-json-p3
- Issue tracker:https://github.com/jg-rp/ruby-json-p3/issues
- Python JSONPath RFC 9535 - A Python implementation of JSONPath that follows RFC 9535 strictly.
- Python JSONPath - Another Python package implementing JSONPath, but with additional features and customization options.
- JSON P3 - RFC 9535 implemented in TypeScript.
find(query, value) -> Array<JSONPathNode>
Apply JSONPath expressionquery to JSON-like datavalue. An array of JSONPathNode instances is returned, one node for each value matched byquery. The returned array will be empty if there were no matches.
EachJSONPathNode has:
- a
valueattribute, which is the JSON-like value associated with the node. - a
locationattribute, which is a nested array of hash/object names and array indices that were required to reach the node's value in the target JSON document. - a
path()method, which returns the normalized path to the node in the target JSON document.
require"json_p3"require"json"data=JSON.parse<<~JSON { "users": [ { "name": "Sue", "score": 100 }, { "name": "Sally", "score": 84, "admin": false }, { "name": "John", "score": 86, "admin": true }, { "name": "Jane", "score": 55 } ], "moderator": "John" }JSONJSONP3.find("$.users[?@.score > 85]",data).eachdo |node|puts"#{node.value} at#{node.path}"end# {"name"=>"Sue", "score"=>100} at $['users'][0]# {"name"=>"John", "score"=>86, "admin"=>true} at $['users'][2]
find_enum(query, value) -> Enumerable<JSONPathNode>
find_enum is an alternative tofind which returns an enumerable (usually an enumerator) ofJSONPathNode instances instead of an array. Depending on the query and the data the query is applied to,find_enum can be more efficient thanfind, especially for large data and queries using recursive descent segments.
# ... continued from aboveJSONP3.find_enum("$.users[?@.score > 85]",data).eachdo |node|puts"#{node.value} at#{node.path}"end# {"name"=>"Sue", "score"=>100} at $['users'][0]# {"name"=>"John", "score"=>86, "admin"=>true} at $['users'][2]
compile(query) -> JSONPath
Prepare a JSONPath expression for repeated application to different JSON-like data. An instance ofJSONPath has afind(data) method, which behaves similarly to the module-levelfind(query, data) method.
require"json_p3"require"json"data=JSON.parse<<~JSON { "users": [ { "name": "Sue", "score": 100 }, { "name": "Sally", "score": 84, "admin": false }, { "name": "John", "score": 86, "admin": true }, { "name": "Jane", "score": 55 } ], "moderator": "John" }JSONpath=JSONP3.compile("$.users[?@.score > 85]")path.find(data).eachdo |node|puts"#{node.value} at#{node.path}"end# {"name"=>"Sue", "score"=>100} at $['users'][0]# {"name"=>"John", "score"=>86, "admin"=>true} at $['users'][2]
match(query, value) -> JSONPathNode | nil
match (aliasfirst) returns a node for the first available match when applyingquery tovalue, ornil if there were no matches.
match?(query, value) -> bool
match? returnstrue if there was at least one match from applyingquery tovalue, orfalse otherwise.
Thefind,find_enum andcompile methods described above are convenience methods equivalent to:
JSONP3::DEFAULT_ENVIRONMENT.find(query, data)JSONP3::DEFAULT_ENVIRONMENT.find_enum(query, data)and
JSONP3::DEFAULT_ENVIRONMENT.compile(query)You could create your own environment like this:
require"json_p3"jsonpath=JSONP3::JSONPathEnvironment.newnodes=jsonpath.find("$.*",{"a"=>"b","c"=>"d"})ppnodes.map(&:value)# ["b", "d"]
To configure an environment with custom filter functions or non-standard selectors, inherit fromJSONPathEnvironment and override some of its constants or the#setup_function_extensions method.
classMyJSONPathEnvironment <JSONP3::JSONPathEnvironment# The maximum integer allowed when selecting array items by index.MAX_INT_INDEX=(2**53) -1# The minimum integer allowed when selecting array items by index.MIN_INT_INDEX= -(2**53) +1# The maximum number of arrays and hashes the recursive descent segment will# traverse before raising a {JSONPathRecursionError}.MAX_RECURSION_DEPTH=100# One of the available implementations of the _name selector_.## - {NameSelector} (the default) will select values from hashes using string keys.# - {SymbolNameSelector} will select values from hashes using string or symbol keys.## Implement your own name selector by inheriting from {NameSelector} and overriding# `#resolve`.NAME_SELECTOR=NameSelector# An implementation of the _index selector_. The default implementation will# select values from arrays only. Implement your own by inheriting from# {IndexSelector} and overriding `#resolve`.INDEX_SELECTOR=IndexSelector# Override this function to configure JSONPath function extensions.# By default, only the standard functions described in RFC 9535 are enabled.defsetup_function_extensions@function_extensions["length"]=Length.new@function_extensions["count"]=Count.new@function_extensions["value"]=Value.new@function_extensions["match"]=Match.new@function_extensions["search"]=Search.newend
JSONPathError is the base class for all JSONPath exceptions. The following classes inherit fromJSONPathError and will only occur when parsing a JSONPath expression, not when applying a path to some data.
JSONPathSyntaxErrorJSONPathTypeErrorJSONPathNameError
JSONPathError implements#detailed_message. With recent versions of Ruby you should get useful error messages.
JSONP3::JSONPathSyntaxError: unexpected trailing whitespace -> '$.foo ' 1:5 |1 | $.foo | ^ unexpected trailing whitespaceresolve(pointer, value) -> Object
Resolve a JSON Pointer (RFC 6901) against some data usingJSONP3.resolve().
require"json_p3"require"json"data=JSON.parse<<~JSON { "users": [ { "name": "Sue", "score": 100 }, { "name": "Sally", "score": 84, "admin": false }, { "name": "John", "score": 86, "admin": true }, { "name": "Jane", "score": 55 } ], "moderator": "John" }JSONputsJSONP3.resolve("/users/1",data)# {"name"=>"Sally", "score"=>84, "admin"=>false}
If a pointer can not be resolved,JSONP3::JSONPointer::UNDEFINED is returned instead. You can use your own default value using thedefault: keyword argument.
# continued from aboveppJSONP3.resolve("/no/such/thing",data,default:nil)# nil
apply(ops, value) -> Object
Apply a JSON Patch (RFC 6902) withJSONP3.apply().Data is modified in place.
require"json"require"json_p3"ops=<<~JSON [ { "op": "add", "path": "/some/foo", "value": { "foo": {} } }, { "op": "add", "path": "/some/foo", "value": { "bar": [] } }, { "op": "copy", "from": "/some/other", "path": "/some/foo/else" }, { "op": "add", "path": "/some/foo/bar/-", "value": 1 } ]JSONdata={"some"=>{"other"=>"thing"}}JSONP3.apply(JSON.parse(ops),data)ppdata# {"some"=>{"other"=>"thing", "foo"=>{"bar"=>[1], "else"=>"thing"}}}
JSONP3.apply(ops, value) is a convenience method equivalent toJSONP3::JSONPatch.new(ops).apply(value). Use theJSONPatch constructor when you need to apply the same patch to different data.
As well as passing an array of hashes following RFC 6902 as ops toJSONPatch, we offer a builder API to construct JSON Patch documents programmatically.
require"json_p3"data={"some"=>{"other"=>"thing"}}patch=JSONP3::JSONPatch.new.add("/some/foo",{"foo"=>[]}).add("/some/foo",{"bar"=>[]}).copy("/some/other","/some/foo/else").copy("/some/foo/else","/some/foo/bar/-")patch.apply(data)ppdata# {"some"=>{"other"=>"thing", "foo"=>{"bar"=>["thing"], "else"=>"thing"}}}
Your contributions and questions are always welcome. Feel free to ask questions, report bugs or request features on theissue tracker or onGithub Discussions. Pull requests are welcome too.
TheJSONPath Compliance Test Suite is included as a gitsubmodule. Clone the JSON P3 git repository and initialize the CTS submodule.
$ git clone git@github.com:jg-rp/ruby-json-p3.git$cd ruby-json-p3$ git submodule update --initWe useBundler andRake. Install development dependencies with:
bundle installRun tests with:
bundle exec rake testLint with:
bundle exec rubocopAnd type check with:
bundle exec steepRun one of the benchmarks with:
bundle exec ruby performance/benchmark_ips.rbDump profile data withbundle exec ruby performance/profile.rb, then generate an HTML flame graph with:
bundle exec stackprof --d3-flamegraph .stackprof-cpu-just-compile.dump > flamegraph-cpu-just-compile.htmlPrint memory usage to the terminal.
bundle exec ruby performance/memory_profile.rbbundle exec rake release andbundle exec rake build will look forgem-private_key.pem andgem-public_cert.pem in~/.gem.
On macOS Sonoma using MacPorts andrbenv,LIBYAML_PREFIX=/opt/local/lib is needed to install TruffleRuby and when executing anybundle command.
About
JSONPath, JSON Patch and JSON Pointer for Ruby
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.