· 6 min read ·

The JSON Tool Sprawl, and What jsongrep Gets Right About the Problem

Source: hackernews

The Problem Was Never Just Performance

Every few months, a new JSON querying tool appears on Hacker News. Some are faster than jq. Some are written in Rust. Some do both. Micah Kepe’s jsongrep is the latest entry, and the discussion it generated (343 points, 216 comments) suggests it touched something real.

But the tooling sprawl in this space isn’t primarily a performance story. It’s a story about mental models. jq is a fully-formed query language. It has closures, recursive descent, string interpolation, format strings, and a reduce operator. It can compute things. That power is also precisely why people reach for alternatives: they don’t want to compute things, they want to find things. And grep already taught them how to do that.

What jq Actually Is

jq was released in 2012 by Stephen Dolan as a command-line JSON processor. Version 1.6 came out in 2018. Then nothing for five years, until v1.7 in October 2023 and v1.7.1 in early 2024, both maintained by the community after Dolan stepped back. The five-year gap meant the community filled the void with alternatives, and those alternatives outlasted their original motivation.

jq’s query language is a functional DSL with pipes:

# Get names of users whose age is over 30
jq '[.users[] | select(.age > 30) | .name]' data.json

This is genuinely expressive. But the cognitive load is real. The .[] iterator, the select function, the | pipe chaining, and the outer [] to collect into an array are all things you need to hold in your head simultaneously. Stack Overflow is littered with “how do I do X in jq” questions where X is something like “filter an array by a field value” that most users expect to be a two-word command.

The error messages don’t help. null (null) and null (null) is a real jq error message. So is Unexpected token at line 1: . for a missing quote somewhere.

The Rust CLI Renaissance

The broader context here is that the last four years have seen a systematic rewrite of Unix CLI staples in Rust. ripgrep replaced grep for many workflows. bat replaced cat. fd replaced find. exa/eza replaced ls. Each of these succeeded not just by being faster, but by having better defaults and UX.

The JSON space is following the same pattern, but it’s messier because jq isn’t just a display tool: it’s a query language, and replacing a query language means either reimplementing it or rethinking it entirely.

jaq takes the first path. It’s a Rust reimplementation of jq that passes the vast majority of jq’s test suite and runs 2-5x faster on comparable queries. If you already know jq and the friction is performance, jaq is the right answer. The syntax is identical.

jsongrep takes the second path.

What jsongrep Does Differently

Rather than reimplementing jq’s DSL, jsongrep adopts grep’s mental model: you give it a pattern, it gives you matches. The query language is regex and path expressions, not a functional pipeline.

# Find any key or value matching "error"
jsongrep 'error' data.json

# Match only on keys
jsongrep -k 'user_.*' data.json

# Match only on values
jsongrep -v '^active$' data.json

# Path-style access
jsongrep -p '.users[].email' data.json

For most day-to-day JSON inspection tasks, this is the right level of expressiveness. When you’re tailing logs and want to find all lines where status is error, you don’t need a Turing-complete language. You need grep, but JSON-aware.

The performance benefits are real and come from the same source: a Rust implementation with a simpler execution model. jq compiles its query language to bytecode and runs an interpreter. jsongrep can use more direct matching strategies because its query model is less expressive. The benchmarks in the article show approximately a 5-20x speedup on large files for the kinds of queries jsongrep is designed for.

The Ecosystem in 2025

It’s worth mapping out the space because different tools genuinely solve different problems:

gron (Go) flattens JSON into greppable assignment statements:

gron data.json | grep 'user.name'
# json.users[0].name = "alice";

You can then pipe the filtered output back through gron --ungron to get valid JSON. This is the purest “stay in the Unix toolkit” approach. The tradeoff is two processes and output that looks like JavaScript.

fx (Go, rewritten from Node.js around v30) is primarily an interactive terminal JSON browser. Its tree view with collapsible nodes, vim keybindings, and live search makes it the right tool for human inspection of complex JSON structures. Non-interactively, it accepts filter expressions, but the interactive mode is the main draw.

jless (Rust) is a read-only JSON pager, in the same interactive-viewer category as fx. It doesn’t query, it displays. Vim keybindings, expandable nodes, a search mode.

yq (Go) uses jq-like syntax but adds YAML, TOML, and XML support. If your workflow involves Kubernetes manifests or Helm charts, yq is probably already in your toolbox, and it handles JSON fine as a secondary format.

miller (C) takes the widest scope: CSV, JSON, TSV, and NDJSON with SQL-like verbs (filter, sort, join, tee). It’s the right tool when your data crosses format boundaries or when you want to join two JSON files.

ToolLanguagePrimary Use Case
jq 1.7.1CFull-featured query/transform
jaqRustjq-compatible, faster
jsongrepRustgrep-style search
gronGoPipe to existing Unix tools
fxGoInteractive exploration
yqGoMulti-format (YAML/JSON/TOML)
millerCMulti-format tabular processing

The Real Trade-off

The fundamental question is whether you’re searching or transforming. These are different operations with different appropriate tools.

jq excels at transformation: reshape this JSON, extract these fields, compute this derived value, convert this nested structure into a flat one. It’s the right tool for pipelines where JSON goes in and different JSON comes out.

jsongrep excels at searching: does this JSON file contain what I’m looking for, and where? It’s the right tool for log analysis, API response debugging, and configuration inspection.

The mistake most people make is reaching for jq for search tasks, then struggling with the syntax, then concluding that jq is bad. jq isn’t bad, it’s just optimized for a different operation. The syntax complexity that seems unnecessary for a simple key lookup is the same complexity that lets you do non-trivial transformations.

The problem is that the search case comes up far more often in practice. When you’re debugging a service, you’re mostly looking for things, not transforming them. The transformation case is real but it tends to happen in scripts, where the cognitive overhead of learning jq syntax is more justified.

What jsongrep’s Reception Says

The Hacker News discussion surfaced a predictable set of reactions: people who use jq daily and find it fine, people who’ve been waiting for exactly this tool, and people suggesting the fifteen other alternatives they use instead. The strong engagement suggests genuine demand for the grep-style mental model.

Rust-based CLI tools have a consistent adoption pattern: they start as “this one weird thing” and end up as the default for their category. ripgrep is now faster than grep in every benchmark and ships with many editors as the default search backend. bat with syntax highlighting is the default viewer in some terminal setups. jaq already has integration with several editors and plugins as a jq backend.

The JSON tooling space is still fragmented in a way that grep and sed never were. Part of that is the inherent complexity of structured data versus text. Part of it is that JSON arrived after the Unix philosophy was established, and nobody has agreed on where it fits. Tools like jsongrep are attempts to pull it back into the grep mental model rather than forward into a new query language paradigm.

For simple inspection tasks, that’s probably right. For the transformation case, jq (or jaq for the speed) remains the appropriate tool. Having both in your PATH and knowing when to reach for each is the practical answer.

Installation is straightforward via Cargo if you’re already in the Rust ecosystem:

cargo install jsongrep

Or check the releases page for precompiled binaries. The project is young but the core use case is narrow enough that it should be stable quickly.

Was this interesting?