{"id":18624060,"url":"https://github.com/mitghi/jetro","last_synced_at":"2025-04-11T03:32:00.542Z","repository":{"id":143516512,"uuid":"603501834","full_name":"mitghi/jetro","owner":"mitghi","description":"Transform, compare and query JSON format","archived":false,"fork":false,"pushed_at":"2024-03-31T18:23:54.000Z","size":201,"stargazers_count":23,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-25T09:02:13.463Z","etag":null,"topics":["jmespath","json","json-path","json-query","json-query-language","json-search","json-transformation","rust","serde","serde-json"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/mitghi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2023-02-18T17:52:42.000Z","updated_at":"2025-03-12T21:47:02.000Z","dependencies_parsed_at":"2024-03-31T19:38:53.437Z","dependency_job_id":null,"html_url":"https://github.com/mitghi/jetro","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitghi%2Fjetro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitghi%2Fjetro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitghi%2Fjetro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitghi%2Fjetro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mitghi","download_url":"https://codeload.github.com/mitghi/jetro/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248335512,"owners_count":21086604,"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":["jmespath","json","json-path","json-query","json-query-language","json-search","json-transformation","rust","serde","serde-json"],"created_at":"2024-11-07T04:27:11.828Z","updated_at":"2025-04-11T03:31:55.514Z","avatar_url":"https://github.com/mitghi.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jetro\n\n[\u003cimg src=\"https://img.shields.io/badge/docs-jetro-blue\"\u003e\u003c/img\u003e](https://docs.rs/jetro)\n[\u003cimg src=\"https://img.shields.io/badge/try-online%20repl-brightgreen\"\u003e\u003c/img\u003e](https://jetro.io)\n![GitHub](https://img.shields.io/github/license/mitghi/jetro)\n\nJetro is a library which provides a custom DSL for transforming, querying and comparing data in JSON format. It is easy to use and extend.\n\nJetro has minimal dependency, the traversal and eval algorithm is implemented on top of [serde_json](https://serde.rs).\n\nJetro can be used inside Web Browser by compiling down to WASM. [Clone it](https://github.com/mitghi/jetroweb) and give it a shot.\n\nJetro can be used in command line using [Jetrocli](https://github.com/mitghi/jetrocli).\n\nJetro combines access path with functions which operate on values matched within the pipeline. Access path uses `/` as separator similar to structure of URI, the start of access path should denote whether the access starts from root by using `\u003e`, it is possible to traverse from root in nested paths by using `\u003c`. \n\nJetro expressions support line breaks and whitespace, the statements can be broken up into smaller parts.\n\nBy convention, functions are denoted using `#` operator. Functions can be composed.\n\n| Function | Action |\n| -------- | ------ |\n| #pick('string' \\| expression, ...) [ as \\| as* 'binding_value' ] | Select a key from an object, bind it to a name, select multiple sub queries to create new object |\n| #head | Head of the list|\n| #tail | Tail of the list |\n| #keys | Keys associated with an object |\n| #values | Values associated with an object |\n| #reverse | Reverse the list |\n| #min | Min value of numbers |\n| #max | Max value of numbers |\n| #all | Whether all boolean values are true |\n| #sum | Sum of numbers |\n| #formats('format with placeholder {} {}', 'key_a', 'key_b') [ -\u003e \\| -\u003e* 'binding_value' ] | Insert formatted key:value into object or return it as single key:value  |\n| #filter('target_key' (\u003e, \u003c, \u003e=, \u003c=, ==, ~=, !=) (string, boolean, number)) | Perform Filter on list |\n| #map(x: x.y.z \\| x.y.z.some_method())| Map each item in a list with the given lambda |\n| #zip | Zip two or more arrays together |\n\n\n```rust\nlet data = serde_json::json!({\n  \"name\": \"mr snuggle\",\n  \"some_entry\": {\n    \"some_obj\": {\n      \"obj\": {\n        \"a\": \"object_a\",\n        \"b\": \"object_b\",\n        \"c\": \"object_c\",\n        \"d\": \"object_d\"\n      }\n    }\n  }\n});\n\nlet mut values = Path::collect(data, \"\u003e/..obj/#pick('a','b')\");\n\n#[derive(Serialize, Deserialize)]\nstruct Output {\n   a: String,\n   b: String,\n}\n\nlet output: Option\u003cOutput\u003e = values.from_index(0);\n```\n#### structure\nJetro consists of a parser, context wrapper which manages traversal and evaluation of each step of user input and a runtime for dynamic functions. The future version will support user-defined functions.\n\n# example\n\n```json\n{\n  \"customer\": {\n    \"id\": \"xyz\",\n    \"ident\": {\n      \"user\": {\n        \"isExternal\": false,\n        \"profile\": {\n          \"firstname\": \"John\",\n          \"alias\": \"Japp\",\n          \"lastname\": \"Appleseed\"\n        }\n      }\n    },\n    \"preferences\": []\n  },\n  \"line_items\": {\n    \"items\": [\n      {\n        \"ident\": \"abc\",\n        \"is_gratis\": false,\n        \"name\": \"pizza\",\n        \"price\": 4.8,\n        \"total\": 1,\n        \"type\": \"base_composable\"\n      },\n      {\n        \"ident\": \"def\",\n        \"is_gratis\": false,\n        \"name\": \"salami\",\n        \"price\": 2.8,\n        \"total\": 10,\n        \"type\": \"ingredient\"\n      },\n      {\n        \"ident\": \"ghi\",\n        \"is_gratis\": false,\n        \"name\": \"cheese\",\n        \"price\": 2,\n        \"total\": 1,\n        \"type\": \"ingredient\"\n      },\n      {\n        \"ident\": \"uip\",\n        \"is_gratis\": true,\n        \"name\": \"chilli\",\n        \"price\": 0,\n        \"total\": 1,\n        \"type\": \"ingredient\"\n      },\n      {\n        \"ident\": \"ewq\",\n        \"is_gratis\": true,\n        \"name\": \"bread sticks\",\n        \"price\": 0,\n        \"total\": 8,\n        \"type\": \"box\"\n      }\n    ]\n  }\n}\n```\n\n### Queries\n\nGet value associated with `line_items`.\n\n```\n\u003e/line_items\n```\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n  ```json\n{\n  \"items\": [\n    {\n      \"ident\": \"abc\",\n      \"is_gratis\": false,\n      \"name\": \"pizza\",\n      \"price\": 4.8,\n      \"total\": 1,\n      \"type\": \"base_composable\"\n    },\n    {\n      \"ident\": \"def\",\n      \"is_gratis\": false,\n      \"name\": \"salami\",\n      \"price\": 2.8,\n      \"total\": 10,\n      \"type\": \"ingredient\"\n    },\n    {\n      \"ident\": \"ghi\",\n      \"is_gratis\": false,\n      \"name\": \"cheese\",\n      \"price\": 2,\n      \"total\": 1,\n      \"type\": \"ingredient\"\n    },\n    {\n      \"ident\": \"uip\",\n      \"is_gratis\": true,\n      \"name\": \"chilli\",\n      \"price\": 0,\n      \"total\": 1,\n      \"type\": \"ingredient\"\n    },\n    {\n      \"ident\": \"ewq\",\n      \"is_gratis\": true,\n      \"name\": \"bread sticks\",\n      \"price\": 0,\n      \"total\": 8,\n      \"type\": \"box\"\n    }\n  ]\n}\n  ```\n\u003c/details\u003e\n\n---\n\nGet value associated with first matching key which has a value and return its `id` field.\n\n```\n\u003e/('non-existing-member' | 'customer')/id\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n\"xyz\"\n```\n\u003c/details\u003e\n\n---\n\nRecursively search for objects that has key with specified value.\n\n```\n\u003e/..('type'='ingredient')\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n\t\"ident\": \"ghi\",\n\t\"is_gratis\": false,\n\t\"name\": \"cheese\",\n\t\"price\": 2,\n\t\"total\": 1,\n\t\"type\": \"ingredient\"\n  },\n  {\n\t\"ident\": \"def\",\n\t\"is_gratis\": false,\n\t\"name\": \"salami\",\n\t\"price\": 2.8,\n\t\"total\": 10,\n\t\"type\": \"ingredient\"\n  }\n]\n```\n\u003c/details\u003e\n\n---\n```\n\u003e/..items/#tail\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n    \"ident\": \"def\",\n    \"is_gratis\": false,\n    \"name\": \"salami\",\n    \"price\": 2.8,\n    \"total\": 10,\n    \"type\": \"ingredient\"\n  },\n  {\n    \"ident\": \"ghi\",\n    \"is_gratis\": false,\n    \"name\": \"cheese\",\n    \"price\": 2,\n    \"total\": 1,\n    \"type\": \"ingredient\"\n  },\n  {\n    \"ident\": \"uip\",\n    \"is_gratis\": true,\n    \"name\": \"chilli\",\n    \"price\": 0,\n    \"total\": 1,\n    \"type\": \"ingredient\"\n  },\n  {\n    \"ident\": \"ewq\",\n    \"is_gratis\": true,\n    \"name\": \"bread sticks\",\n    \"price\": 0,\n    \"total\": 8,\n    \"type\": \"box\"\n  }\n]\n```\n\u003c/details\u003e\n\n---\n\n```\n\u003e/..items/#filter('is_gratis' == true and 'name' ~= 'ChILLi')\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n    \"ident\": \"uip\",\n    \"is_gratis\": true,\n    \"name\": \"chilli\",\n    \"price\": 0,\n    \"total\": 1,\n    \"type\": \"ingredient\"\n  }\n]\n```\n\u003c/details\u003e\n\n---\n\n```\n\u003e/..items/#filter('is_gratis' == true and 'name' ~= 'ChILLi')/#map(x: x.type)\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  \"ingredient\"\n]\n```\n\u003c/details\u003e\n\n---\n\nCreate a new object with scheme `{'total': ..., 'fullname': ...}` as follow:\n- recursively search for `line_items`, dive into any matched object, filter matches with `is_gratis == false` statement, recursively look for their prices and return the sum of prices\n- recursively search for object `user`, select its `profile` and create a new object with schema `{'fullname': ...}` formated by concatenating values of keys ('firstname', 'lastname')\n\n```\n\u003e/#pick(\n  \u003e/..line_items\n   /*\n   /#filter('is_gratis' == false)/..price/#sum as 'total',\n\n  \u003e/..user\n   /profile\n   /#formats('{} {}', 'firstname', 'lastname') -\u003e* 'fullname'\n)\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n{\n  \"fullname\": \"John Appleseed\",\n  \"total\": 9.6\n}\n```\n\u003c/details\u003e\n\n---\n\nSelect up to 4 items from index zero of array `items`\n\n```\n\u003e/..items/[:4]\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n    \"ident\": \"abc\",\n    \"is_gratis\": false,\n    \"name\": \"pizza\",\n    \"price\": 4.8,\n    \"total\": 1,\n    \"type\": \"base_composable\"\n  },\n  {\n    \"ident\": \"def\",\n    \"is_gratis\": false,\n    \"name\": \"salami\",\n    \"price\": 2.8,\n    \"total\": 10,\n    \"type\": \"ingredient\"\n  },\n  {\n    \"ident\": \"ghi\",\n    \"is_gratis\": false,\n    \"name\": \"cheese\",\n    \"price\": 2,\n    \"total\": 1,\n    \"type\": \"ingredient\"\n  },\n  {\n    \"ident\": \"uip\",\n    \"is_gratis\": true,\n    \"name\": \"chilli\",\n    \"price\": 0,\n    \"total\": 1,\n    \"type\": \"ingredient\"\n  }\n]\n```\n\u003c/details\u003e\n\n---\n\nSelect from 4th index and consume until end of array `items`\n\n```\n\u003e/..items/[4:]\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n    \"ident\": \"ewq\",\n    \"is_gratis\": true,\n    \"name\": \"bread sticks\",\n    \"price\": 0,\n    \"total\": 8,\n    \"type\": \"box\"\n  }\n]\n```\n\u003c/details\u003e\n\n---\n\nCreate a new object with schema `{'total_gratis': ...}` as follow:\n- Recursively look for any object containing `items`, and then recursively search within the matched object for `is_gratis` and length of matched values\n\n```\n\u003e/#pick(\u003e/..items/..is_gratis/#len as 'total_gratis')\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n{\n  \"total_gratis\": 2\n}\n```\n\u003c/details\u003e\n\n---\n\nRecursively search for object `items`, select its first item and return its keys\n\n```\n\u003e/..items/[0]/#keys\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  \"ident\",\n  \"is_gratis\",\n  \"name\",\n  \"price\",\n  \"total\",\n  \"type\"\n]\n```\n\u003c/details\u003e\n\n---\n\nRecursively search for object `items`, select its first item and return its values\n\n```\n\u003e/..items/[0]/#values\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  \"abc\",\n  false,\n  \"pizza\",\n  4.8,\n  1,\n  \"base_composable\"\n]\n```\n\u003c/details\u003e\n\n---\n\nZip two or more arrays together.\n\n\n```\n\u003e/#pick(\u003e/..name as 'name',\n        \u003e/..nested as 'field',\n        \u003e/..b as 'release')/#zip\n```\n\nJSON:\n\n```json\n{\n  \"a\": [\n    {\n      \"name\": \"tool\",\n      \"value\": {\n        \"nested\": \"field\"\n      }\n    },\n    {\n      \"name\": \"pneuma\",\n      \"value\": {\n        \"nested\": \"seal\"\n      }\n    }\n  ],\n  \"b\": [\n    2000,\n    2100\n  ]\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee output\u003c/summary\u003e\n\n  ### result\n\n```json\n[\n  {\n    \"field\": \"field\",\n    \"name\": \"tool\",\n    \"release\": 2000\n  },\n  {\n    \"field\": \"seal\",\n    \"name\": \"pneuma\",\n    \"release\": 2100\n  }\n]\n```\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitghi%2Fjetro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmitghi%2Fjetro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitghi%2Fjetro/lists"}