Skip to content

sen-ltd/json-flatten

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

json-flatten

Flatten nested JSON into greppable, diffable, spreadsheet-friendly path = value lines. A small single-binary Rust tool in the spirit of gron, with a handful of extra output formats and a bidirectional --invert mode.

$ echo '{"users":[{"name":"alice","age":30},{"name":"bob","age":25}],"total":2}' | json-flatten -
users[0].name = "alice"
users[0].age = 30
users[1].name = "bob"
users[1].age = 25
total = 2

Why

Pretty-printed JSON is hostile to the two things you actually do with JSON on the command line:

  1. Grep it. With pretty output you can't grep email because the line for email doesn't contain the object that owns it. With flat output every line is a full path from the root — grep just works, and the result tells you where the match lives.
  2. Diff it. diff a.json b.json on pretty-printed JSON is noisy and order-sensitive. diff <(json-flatten --sort-keys a.json) <(json-flatten --sort-keys b.json) is small, stable, and reviewable.

A third handy case: loading JSON into a spreadsheet via --format tsv, or dumping a config into a shell env file via --format env.

Install

# From source
cargo install --path .

# Or via Docker
docker build -t json-flatten .
docker run --rm -i json-flatten - < data.json

Most people will want to alias it:

alias jf=json-flatten

Usage

json-flatten [OPTIONS] [FILE]
  • FILE is a JSON file, or - for stdin. Omit it to read stdin.

Output formats

--format Example Good for
dots (default) users[0].name = "alice" grep, diff, reading
jsonpath $.users[0].name = "alice" strict JSONPath consumers
env USERS_0_NAME=alice shell env files
tsv users[0].name\talice spreadsheets, awk
json [{"path":"users[0].name","value":"alice","type":"string"}] feeding another JSON tool

Useful flags

  • --no-color — disable ANSI color (auto-detected via isatty and NO_COLOR).
  • --path PATH — start at a sub-path, e.g. --path users[0].
  • --depth N — stop recursion at N levels; the rest is emitted as a literal.
  • --sort-keys — deterministic output.
  • --include GLOB / --exclude GLOB — filter paths (may repeat).
  • --invert / -u — read dots output and rebuild the original JSON.

The round-trip

echo '{"a":{"b":{"c":1}}}' | json-flatten - | json-flatten - --invert
# {
#   "a": { "b": { "c": 1 } }
# }

flatten → invert is a true round-trip for everything flatten produces. That includes weird keys (with spaces, dots, or quotes) because the path quoting is chosen so the parser can unambiguously recover the original key.

Path escaping

An object key is rendered unquoted only when it's a "bare identifier" — starts with a letter or underscore, contains only letters, digits, and underscores. Anything else gets bracket-quoted with JSON-style string escapes:

foo.bar                # bare
["a.b"]                # key with a dot
["with \"quote\""]     # key with a quote
["日本語"]             # non-ASCII key

This is the same mechanism --invert uses to parse back, so round-tripping works for keys that would otherwise collide with the path syntax.

The env-format collision problem

--format env is lossy by design. {"db.host": "a"} and {"db": {"host": "a"}} both produce DB_HOST=a. The uppercased-and-underscored key space is much smaller than the JSON key space. If you're going to re-read the JSON structure, use dots or json. If you just want a shell env file, env is fine — just don't round-trip it.

Tradeoffs

  • No streaming. Input is read into memory and parsed in one shot. If you have 500 MB of JSON, use jq with -c and split.
  • Numbers round-trip via serde_json::Number, which means scientific notation is preserved but integer vs float is distinguished. Infinity and NaN don't exist in JSON, so they can't appear.
  • No JSONC or JSON5. Input must be strict JSON.
  • Path quoting uses double quotes, not backticks. This lets the output survive being re-parsed as JSON-literal strings in other tools.

Exit codes

  • 0 — success
  • 1 — bad JSON, path not found, invert failure
  • 2 — bad arguments

License

MIT.

Links

About

A gron-style JSON flattener written in Rust as a 9.48 MB alpine single binary, with four output formats instead of one and a bidirectional --invert (ungron) mode that's been tested on a non-trivial round-trip.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors