{"id":18732328,"url":"https://github.com/jg-rp/ruby-json-p3","last_synced_at":"2025-04-12T18:31:07.415Z","repository":{"id":259374457,"uuid":"872369581","full_name":"jg-rp/ruby-json-p3","owner":"jg-rp","description":"JSONPath, JSON Patch and JSON Pointer for Ruby","archived":false,"fork":false,"pushed_at":"2025-03-18T16:34:49.000Z","size":235,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T11:55:11.279Z","etag":null,"topics":["json","jsonpath","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jg-rp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-14T10:15:43.000Z","updated_at":"2025-03-18T16:34:52.000Z","dependencies_parsed_at":"2025-03-06T09:26:49.888Z","dependency_job_id":"8a326ac4-8dff-40e2-b5ac-99085ae152d5","html_url":"https://github.com/jg-rp/ruby-json-p3","commit_stats":null,"previous_names":["jg-rp/ruby-json-p3","jg-rp/ruby-jsonpath-rfc9535"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jg-rp%2Fruby-json-p3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jg-rp%2Fruby-json-p3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jg-rp%2Fruby-json-p3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jg-rp%2Fruby-json-p3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jg-rp","download_url":"https://codeload.github.com/jg-rp/ruby-json-p3/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248613275,"owners_count":21133482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","jsonpath","ruby"],"created_at":"2024-11-07T15:05:21.280Z","updated_at":"2025-04-12T18:31:07.409Z","avatar_url":"https://github.com/jg-rp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eJSONPath, JSON Patch and JSON Pointer for Ruby\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nWe follow \u003ca href=\"https://datatracker.ietf.org/doc/html/rfc9535\"\u003eRFC 9535\u003c/a\u003e strictly and test against the \u003ca href=\"https://github.com/jsonpath-standard/jsonpath-compliance-test-suite\"\u003eJSONPath Compliance Test Suite\u003c/a\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/jg-rp/ruby-json-p3/blob/main/LICENSE.txt\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/l/jsonpath-rfc9535.svg?style=flat-square\" alt=\"License\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/jg-rp/ruby-json-p3/actions\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/jg-rp/ruby-json-p3/main.yml?branch=main\u0026label=tests\u0026style=flat-square\" alt=\"Tests\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://rubygems.org/gems/json_p3\"\u003e\n    \u003cimg alt=\"Gem Version\" src=\"https://img.shields.io/gem/v/json_p3?style=flat-square\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/jg-rp/ruby-json-p3\"\u003e\n    \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/Ruby-3.1%20%7C%203.2%20%7C%203.3%20%7C%203.4-CC342D?style=flat-square\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n**Table of Contents**\n\n- [Install](#install)\n- [Example](#example)\n- [Links](#links)\n- [Related projects](#related-projects)\n- [Quick start](#quick-start)\n- [Contributing](#contributing)\n\n## Install\n\nAdd `'json_p3'` to your Gemfile:\n\n```\ngem 'json_p3', '~\u003e 0.4.1'\n```\n\nOr\n\n```\ngem install json_p3\n```\n\n### Checksum\n\nJSON 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:\n\n```\ngem cert --add \u003c(curl -Ls https://raw.githubusercontent.com/jg-rp/ruby-json-p3/refs/heads/main/certs/jgrp.pem)\n```\n\nFollowed by:\n\n```\ngem install json_p3 -P MediumSecurity\n```\n\nJSON P3 has no runtime dependencies, so `-P HighSecurity` is OK too. See https://guides.rubygems.org/security/ for more information.\n\n## Example\n\n```ruby\nrequire \"json_p3\"\nrequire \"json\"\n\ndata = JSON.parse \u003c\u003c~JSON\n  {\n    \"users\": [\n      {\n        \"name\": \"Sue\",\n        \"score\": 100\n      },\n      {\n        \"name\": \"Sally\",\n        \"score\": 84,\n        \"admin\": false\n      },\n      {\n        \"name\": \"John\",\n        \"score\": 86,\n        \"admin\": true\n      },\n      {\n        \"name\": \"Jane\",\n        \"score\": 55\n      }\n    ],\n    \"moderator\": \"John\"\n  }\nJSON\n\nJSONP3.find(\"$.users[?@.score \u003e 85]\", data).each do |node|\n  puts node.value\nend\n\n# {\"name\"=\u003e\"Sue\", \"score\"=\u003e100}\n# {\"name\"=\u003e\"John\", \"score\"=\u003e86, \"admin\"=\u003etrue}\n```\n\nOr, reading JSON data from a file:\n\n```ruby\nrequire \"json_p3\"\nrequire \"json\"\n\ndata = JSON.load_file(\"/path/to/some.json\")\n\nJSONP3.find(\"$.some.query\", data).each do |node|\n  puts node.value\nend\n```\n\nYou could read data from a YAML formatted file too, or any data format that can be loaded into hashes and arrays.\n\n```ruby\nrequire \"json_p3\"\nrequire \"yaml\"\n\ndata = YAML.load_file(\"/tmp/some.yaml\")\n\nJSONP3.find(\"$.users[?@.score \u003e 85]\", data).each do |node|\n  puts node.value\nend\n```\n\n## Links\n\n- Change log: https://github.com/jg-rp/ruby-json-p3/blob/main/CHANGELOG.md\n- RubyGems: https://rubygems.org/gems/json_p3\n- Source code: https://github.com/jg-rp/ruby-json-p3\n- Issue tracker: https://github.com/jg-rp/ruby-json-p3/issues\n\n## Related projects\n\n- [Python JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - A Python implementation of JSONPath that follows RFC 9535 strictly.\n- [Python JSONPath](https://github.com/jg-rp/python-jsonpath) - Another Python package implementing JSONPath, but with additional features and customization options.\n- [JSON P3](https://github.com/jg-rp/json-p3) - RFC 9535 implemented in TypeScript.\n\n## Quick start\n\n### find\n\n`find(query, value) -\u003e Array\u003cJSONPathNode\u003e`\n\nApply JSONPath expression _query_ to JSON-like data _value_. An array of JSONPathNode instances is returned, one node for each value matched by _query_. The returned array will be empty if there were no matches.\n\nEach `JSONPathNode` has:\n\n- a `value` attribute, which is the JSON-like value associated with the node.\n- a `location` attribute, 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.\n- a `path()` method, which returns the normalized path to the node in the target JSON document.\n\n```ruby\nrequire \"json_p3\"\nrequire \"json\"\n\ndata = JSON.parse \u003c\u003c~JSON\n  {\n    \"users\": [\n      {\n        \"name\": \"Sue\",\n        \"score\": 100\n      },\n      {\n        \"name\": \"Sally\",\n        \"score\": 84,\n        \"admin\": false\n      },\n      {\n        \"name\": \"John\",\n        \"score\": 86,\n        \"admin\": true\n      },\n      {\n        \"name\": \"Jane\",\n        \"score\": 55\n      }\n    ],\n    \"moderator\": \"John\"\n  }\nJSON\n\nJSONP3.find(\"$.users[?@.score \u003e 85]\", data).each do |node|\n  puts \"#{node.value} at #{node.path}\"\nend\n\n# {\"name\"=\u003e\"Sue\", \"score\"=\u003e100} at $['users'][0]\n# {\"name\"=\u003e\"John\", \"score\"=\u003e86, \"admin\"=\u003etrue} at $['users'][2]\n```\n\n### find_enum\n\n`find_enum(query, value) -\u003e Enumerable\u003cJSONPathNode\u003e`\n\n`find_enum` is an alternative to `find` which returns an enumerable (usually an enumerator) of `JSONPathNode` instances instead of an array. Depending on the query and the data the query is applied to, `find_enum` can be more efficient than `find`, especially for large data and queries using recursive descent segments.\n\n```ruby\n# ... continued from above\n\nJSONP3.find_enum(\"$.users[?@.score \u003e 85]\", data).each do |node|\n  puts \"#{node.value} at #{node.path}\"\nend\n\n# {\"name\"=\u003e\"Sue\", \"score\"=\u003e100} at $['users'][0]\n# {\"name\"=\u003e\"John\", \"score\"=\u003e86, \"admin\"=\u003etrue} at $['users'][2]\n```\n\n### compile\n\n`compile(query) -\u003e JSONPath`\n\nPrepare a JSONPath expression for repeated application to different JSON-like data. An instance of `JSONPath` has a `find(data)` method, which behaves similarly to the module-level `find(query, data)` method.\n\n```ruby\nrequire \"json_p3\"\nrequire \"json\"\n\ndata = JSON.parse \u003c\u003c~JSON\n  {\n    \"users\": [\n      {\n        \"name\": \"Sue\",\n        \"score\": 100\n      },\n      {\n        \"name\": \"Sally\",\n        \"score\": 84,\n        \"admin\": false\n      },\n      {\n        \"name\": \"John\",\n        \"score\": 86,\n        \"admin\": true\n      },\n      {\n        \"name\": \"Jane\",\n        \"score\": 55\n      }\n    ],\n    \"moderator\": \"John\"\n  }\nJSON\n\npath = JSONP3.compile(\"$.users[?@.score \u003e 85]\")\n\npath.find(data).each do |node|\n  puts \"#{node.value} at #{node.path}\"\nend\n\n# {\"name\"=\u003e\"Sue\", \"score\"=\u003e100} at $['users'][0]\n# {\"name\"=\u003e\"John\", \"score\"=\u003e86, \"admin\"=\u003etrue} at $['users'][2]\n```\n\n### match / first\n\n`match(query, value) -\u003e JSONPathNode | nil`\n\n`match` (alias `first`) returns a node for the first available match when applying _query_ to _value_, or `nil` if there were no matches.\n\n### match?\n\n`match?(query, value) -\u003e bool`\n\n`match?` returns `true` if there was at least one match from applying _query_ to _value_, or `false` otherwise.\n\n### JSONPathEnvironment\n\nThe `find`, `find_enum` and `compile` methods described above are convenience methods equivalent to:\n\n```\nJSONP3::DEFAULT_ENVIRONMENT.find(query, data)\n```\n\n```\nJSONP3::DEFAULT_ENVIRONMENT.find_enum(query, data)\n```\n\nand\n\n```\nJSONP3::DEFAULT_ENVIRONMENT.compile(query)\n```\n\nYou could create your own environment like this:\n\n```ruby\nrequire \"json_p3\"\n\njsonpath = JSONP3::JSONPathEnvironment.new\nnodes = jsonpath.find(\"$.*\", { \"a\" =\u003e \"b\", \"c\" =\u003e \"d\" })\npp nodes.map(\u0026:value) # [\"b\", \"d\"]\n```\n\nTo configure an environment with custom filter functions or non-standard selectors, inherit from `JSONPathEnvironment` and override some of its constants or the `#setup_function_extensions` method.\n\n```ruby\nclass MyJSONPathEnvironment \u003c JSONP3::JSONPathEnvironment\n  # The maximum integer allowed when selecting array items by index.\n  MAX_INT_INDEX = (2**53) - 1\n\n  # The minimum integer allowed when selecting array items by index.\n  MIN_INT_INDEX = -(2**53) + 1\n\n  # The maximum number of arrays and hashes the recursive descent segment will\n  # traverse before raising a {JSONPathRecursionError}.\n  MAX_RECURSION_DEPTH = 100\n\n  # One of the available implementations of the _name selector_.\n  #\n  # - {NameSelector} (the default) will select values from hashes using string keys.\n  # - {SymbolNameSelector} will select values from hashes using string or symbol keys.\n  #\n  # Implement your own name selector by inheriting from {NameSelector} and overriding\n  # `#resolve`.\n  NAME_SELECTOR = NameSelector\n\n  # An implementation of the _index selector_. The default implementation will\n  # select values from arrays only. Implement your own by inheriting from\n  # {IndexSelector} and overriding `#resolve`.\n  INDEX_SELECTOR = IndexSelector\n\n  # Override this function to configure JSONPath function extensions.\n  # By default, only the standard functions described in RFC 9535 are enabled.\n  def setup_function_extensions\n    @function_extensions[\"length\"] = Length.new\n    @function_extensions[\"count\"] = Count.new\n    @function_extensions[\"value\"] = Value.new\n    @function_extensions[\"match\"] = Match.new\n    @function_extensions[\"search\"] = Search.new\n  end\n```\n\n### JSONPathError\n\n`JSONPathError` is the base class for all JSONPath exceptions. The following classes inherit from `JSONPathError` and will only occur when parsing a JSONPath expression, not when applying a path to some data.\n\n- `JSONPathSyntaxError`\n- `JSONPathTypeError`\n- `JSONPathNameError`\n\n`JSONPathError` implements `#detailed_message`. With recent versions of Ruby you should get useful error messages.\n\n```\nJSONP3::JSONPathSyntaxError: unexpected trailing whitespace\n  -\u003e '$.foo ' 1:5\n  |\n1 | $.foo\n  |      ^ unexpected trailing whitespace\n```\n\n### resolve\n\n`resolve(pointer, value) -\u003e Object`\n\nResolve a JSON Pointer (RFC 6901) against some data using `JSONP3.resolve()`.\n\n```ruby\nrequire \"json_p3\"\nrequire \"json\"\n\ndata = JSON.parse \u003c\u003c~JSON\n  {\n    \"users\": [\n      {\n        \"name\": \"Sue\",\n        \"score\": 100\n      },\n      {\n        \"name\": \"Sally\",\n        \"score\": 84,\n        \"admin\": false\n      },\n      {\n        \"name\": \"John\",\n        \"score\": 86,\n        \"admin\": true\n      },\n      {\n        \"name\": \"Jane\",\n        \"score\": 55\n      }\n    ],\n    \"moderator\": \"John\"\n  }\nJSON\n\nputs JSONP3.resolve(\"/users/1\", data)\n# {\"name\"=\u003e\"Sally\", \"score\"=\u003e84, \"admin\"=\u003efalse}\n```\n\nIf a pointer can not be resolved, `JSONP3::JSONPointer::UNDEFINED` is returned instead. You can use your own default value using the `default:` keyword argument.\n\n```ruby\n# continued from above\n\npp JSONP3.resolve(\"/no/such/thing\", data, default: nil) # nil\n```\n\n### apply\n\n`apply(ops, value) -\u003e Object`\n\nApply a JSON Patch ([RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)) with `JSONP3.apply()`. **Data is modified in place**.\n\n```ruby\nrequire \"json\"\nrequire \"json_p3\"\n\nops = \u003c\u003c~JSON\n  [\n    { \"op\": \"add\", \"path\": \"/some/foo\", \"value\": { \"foo\": {} } },\n    { \"op\": \"add\", \"path\": \"/some/foo\", \"value\": { \"bar\": [] } },\n    { \"op\": \"copy\", \"from\": \"/some/other\", \"path\": \"/some/foo/else\" },\n    { \"op\": \"add\", \"path\": \"/some/foo/bar/-\", \"value\": 1 }\n  ]\nJSON\n\ndata = { \"some\" =\u003e { \"other\" =\u003e \"thing\" } }\nJSONP3.apply(JSON.parse(ops), data)\npp data\n# {\"some\"=\u003e{\"other\"=\u003e\"thing\", \"foo\"=\u003e{\"bar\"=\u003e[1], \"else\"=\u003e\"thing\"}}}\n```\n\n`JSONP3.apply(ops, value)` is a convenience method equivalent to `JSONP3::JSONPatch.new(ops).apply(value)`. Use the `JSONPatch` constructor when you need to apply the same patch to different data.\n\nAs well as passing an array of hashes following RFC 6902 as ops to `JSONPatch`, we offer a builder API to construct JSON Patch documents programmatically.\n\n```ruby\nrequire \"json_p3\"\n\ndata = { \"some\" =\u003e { \"other\" =\u003e \"thing\" } }\n\npatch = JSONP3::JSONPatch.new\n                         .add(\"/some/foo\", { \"foo\" =\u003e [] })\n                         .add(\"/some/foo\", { \"bar\" =\u003e [] })\n                         .copy(\"/some/other\", \"/some/foo/else\")\n                         .copy(\"/some/foo/else\", \"/some/foo/bar/-\")\n\npatch.apply(data)\npp data\n# {\"some\"=\u003e{\"other\"=\u003e\"thing\", \"foo\"=\u003e{\"bar\"=\u003e[\"thing\"], \"else\"=\u003e\"thing\"}}}\n```\n\n## Contributing\n\nYour contributions and questions are always welcome. Feel free to ask questions, report bugs or request features on the [issue tracker](https://github.com/jg-rp/ruby-json-p3/issues) or on [Github Discussions](https://github.com/jg-rp/ruby-json-p3/discussions). Pull requests are welcome too.\n\n### Development\n\nThe [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite) is included as a git [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules). Clone the JSON P3 git repository and initialize the CTS submodule.\n\n```shell\n$ git clone git@github.com:jg-rp/ruby-json-p3.git\n$ cd ruby-json-p3\n$ git submodule update --init\n```\n\nWe use [Bundler](https://bundler.io/) and [Rake](https://ruby.github.io/rake/). Install development dependencies with:\n\n```\nbundle install\n```\n\nRun tests with:\n\n```\nbundle exec rake test\n```\n\nLint with:\n\n```\nbundle exec rubocop\n```\n\nAnd type check with:\n\n```\nbundle exec steep\n```\n\nRun one of the benchmarks with:\n\n```\nbundle exec ruby performance/benchmark_ips.rb\n```\n\n### Profiling\n\n#### CPU profile\n\nDump profile data with `bundle exec ruby performance/profile.rb`, then generate an HTML flame graph with:\n\n```\nbundle exec stackprof --d3-flamegraph .stackprof-cpu-just-compile.dump \u003e flamegraph-cpu-just-compile.html\n```\n\n#### Memory profile\n\nPrint memory usage to the terminal.\n\n```\nbundle exec ruby performance/memory_profile.rb\n```\n\n### Notes to self\n\n#### Build\n\n`bundle exec rake release` and `bundle exec rake build` will look for `gem-private_key.pem` and `gem-public_cert.pem` in `~/.gem`.\n\n#### TruffleRuby\n\nOn macOS Sonoma using MacPorts and `rbenv`, `LIBYAML_PREFIX=/opt/local/lib` is needed to install TruffleRuby and when executing any `bundle` command.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjg-rp%2Fruby-json-p3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjg-rp%2Fruby-json-p3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjg-rp%2Fruby-json-p3/lists"}