CLI tool
micahkepe/jsongrep avatar
micahkepe/jsongrep

jsongrep: Regular Path Expressions for JSON, YAML, TOML, CBOR and MessagePack

A path query language for JSON, YAML, TOML, and other serialization formats.

670 stars12 forksRustMIT

At a glance

What is it?
jsongrep compiles a path query into a DFA and matches it against a document tree, printing where each match was found. It is a declarative alternative to jq for path selection, with a narrower scope than a general transformation language.
Who is it for?
Adopt jsongrep if your job is selecting paths out of structured documents and you want the matched location printed alongside the value, or if you need the same query to run over JSON, YAML, TOML, JSONL, CBOR and MessagePack. Do not adopt it as a replacement for jq when you need to construct new objects, aggregate values, or write multi-step transformations; the README frames jsongrep as a matching engine, not a transformation pipeline.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem jsongrep addresses: selecting paths, not transforming documents

The README draws the distinction directly. Tools like jq use a filter pipeline to transform data; jsongrep asks you to declare what paths to match rather than describe how to transform. The README puts it as: "you declare what paths to match rather than describing how to transform." That framing matters because it bounds the tool. If your task is to reshape a document, compute a sum, or build a new object, jsongrep is the wrong shape of tool. If your task is to find every occurrence of a field, every element of an array, or every field under one of several keys, jsongrep expresses that as a single pattern instead of a chain of filters. The intended audience is anyone who writes shell pipelines over structured data and finds themselves repeating the same recursive descent idiom, plus Rust developers who want the same matching behaviour as a library rather than a process.

How the matching engine works: queries compile to a DFA over a document tree

The README describes JSON documents as trees: objects and arrays branch into nested values, and edges are labeled by field names or array indices. A jsongrep query describes a set of paths through that tree using regular expression operators, and the README states the query compiles to a DFA that processes the document. So the model is a finite automaton walking labeled edges, accepting when it reaches a path in the set the pattern describes. The four operators named in the README are the Kleene star (`**.name`), the wildcard (`users[*].email`), disjunction (`(error|warn).*`), and a combined any-depth form (`(* | [*])*.name`) that matches through both objects and arrays. Non-JSON input formats are converted to JSON at the boundary and then queried with the same engine, which is why the README says queries work identically regardless of input format. That conversion step means format support is a front-end concern, not a second query engine.

Getting it running: install paths and the first query

The README lists five installation methods. Homebrew and cargo are the two most likely for a developer machine: `brew install jsongrep` and `cargo install jsongrep`. Winget and Scoop cover Windows with `winget install jsongrep` and `scoop install jsongrep`. The binary is invoked as `jg`. The README's first example pipes the Nobel Prize API into a query: `curl -s https://api.nobelprize.org/v1/prize.json | jg 'prizes[0].laureates[*].firstname'`. Inline JSON works the same way: `echo '{"users": [{"name": "Alice"}, {"name": "Bob"}]}' | jg 'users.[*].name'`. Two flags appear in the README's examples and are worth knowing before you write your first pipeline. `-F` treats the query as a literal field name at any depth, so `jg -F firstname` is a shorthand for a recursive descent that would otherwise need `**`. `--count` prints a total instead of the matches, shown as `Found matches: 1026` in the Nobel example. An empty query string pretty-prints the document, which the README demonstrates with `echo '{"name":"Ada","age":36}' | jg ''`.

Output carries the matched path, and that behaviour changes when you pipe

This is the detail most likely to surprise someone wiring jsongrep into a script. Interactive output includes a header naming the location of each match, for example `prizes.[0].laureates.[0].firstname:` followed by the value on the next line. The README states that jq does not show where each match was found and jsongrep does. It then adds the caveat: examples show terminal output, and when piped, path headers are hidden by default. Two flags control this, `--with-path` and `--no-path`. So a query that reads cleanly in a terminal can produce different output the moment it lands in a pipe, and if your downstream parser expects the header line you need `--with-path` explicitly. The README does not spell out the exact header format for every output mode, so if you are parsing jsongrep output programmatically, confirm the format on your own data before relying on it.

The jq comparison, and where jsongrep is the weaker choice

The README's own comparison is the honest place to start. For finding a field at any depth, jsongrep uses `jg -F firstname` while jq needs `.. | .firstname? // empty`, including the null-suppression operator. For selecting several fields at once, jsongrep uses a disjunction, `jg 'prizes[0].(year|category)'`, while jq lists each field, `.prizes[0] | .year, .category`. For counting, jsongrep adds `--count` while jq wraps the expression in an array constructor and takes its length. The pattern is consistent: jsongrep is shorter for selection, jq is the general language. That is the limitation. jsongrep has no documented facility for constructing new objects, for arithmetic, for grouping, or for the multi-stage reshaping that jq handles routinely. If your pipeline needs to emit a reshaped document rather than a set of matched paths, jsongrep is the wrong tool and the README does not claim otherwise. The other limitation is the one the README flags for its own benchmarks: where a tool lacks a feature, the benchmark is skipped rather than faked, which means the comparison tables do not cover every capability pair.

Alternatives: jq for transformation, JSONPath and JMESPath for embedded queries

jq is the direct alternative and the README treats it as the reference point. The difference in approach is pipeline versus pattern: jq composes filters that each take input and produce output, so a query is a program; jsongrep compiles a single pattern to a DFA and reports which paths it accepts. jq can do things jsongrep cannot, and jsongrep expresses recursive field selection more briefly than jq. The README also names jsonpath-rust, jmespath, jaq and jql as benchmark peers. Those matter as alternatives in a different sense: JSONPath and JMESPath are query languages you would embed in an application rather than reach for in a shell, and jaq is a jq implementation in Rust. If your constraint is that the query must live inside a Rust program, jsongrep ships as a library as well as a binary, which the README lists under Library Usage, so the choice between jsongrep and jsonpath-rust is partly about whether you want the regular-path operator set and the multi-format front end, or a JSONPath-compatible surface.

Benchmarks, releases and what the material does not establish

The README describes a Criterion-based benchmark suite with four groups that isolate different costs: `document_parse`, `query_compile`, `query_search`, and `end_to_end`. Test data runs from a small sample up to a 190 MB GeoJSON file, citylots.json, and the README links interactive Criterion reports and a methodology document under benches/README.md. What the README does not give in the text is a headline number for any tool, so no speed claim can be repeated here without reading the linked reports. The benchmark design is the more interesting part: separating parse, compile and search means a slow compile step would be visible rather than hidden inside an end-to-end figure, and the decision to skip rather than fake benchmarks for missing features is a methodological choice worth noting. On maintenance, the repository metadata shows releases at v0.8.1 in March 2026, v0.9.0 in April 2026, and v0.10.0 in August 2026, with the last push in August 2026 and the project not archived. That is a cadence of roughly one minor release per quarter across the visible window, though three releases is too short a history to project from. The licence is MIT, which permits commercial use and modification provided the copyright notice and permission notice are retained; this is a description of the licence text, not legal advice, and you should read the LICENSE file in the repository for the operative terms.

Editorial conclusion

Adopt jsongrep if your job is selecting paths out of structured documents and you want the matched location printed alongside the value, or if you need the same query to run over JSON, YAML, TOML, JSONL, CBOR and MessagePack. Do not adopt it as a replacement for jq when you need to construct new objects, aggregate values, or write multi-step transformations; the README frames jsongrep as a matching engine, not a transformation pipeline. Before committing, verify two things against your own data: that the recursive-descent query you intend to use does not expand into more matches than you expect, and that the path headers you see in a terminal are still present when you pipe output, since the README states headers are hidden by default when piped and are controlled by --with-path and --no-path.

Official sources

  1. License: MIT
  2. micahkepe/jsongrep on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes