Skip to content

Working with Files

jpx provides several ways to read input and write output, making it flexible for different workflows.

Reading Input

From stdin (default)

Pipe JSON directly to jpx:

echo '{"name": "alice"}' | jpx 'name'
# "alice"

cat data.json | jpx 'users[*].name'

curl -s https://api.example.com/data | jpx 'items[0]'

From a File

Pass the file as a trailing argument (jq-style):

jpx 'users[*].name' data.json

jpx 'settings.theme' config.json

Or use -f / --file explicitly:

jpx -f data.json 'users[*].name'

jpx --file config.json 'settings.theme'

Null Input

Use -n or --null-input for expressions that don't need input data:

jpx -n 'now()'
# "2024-01-15T10:30:00Z"

jpx -n 'uuid()'
# "550e8400-e29b-41d4-a716-446655440000"

jpx -n 'range(1, 5)'
# [1, 2, 3, 4]

Writing Output

To stdout (default)

Output goes to stdout with pretty-printing by default:

jpx -f data.json 'users[0]'
# {
#   "name": "alice",
#   "age": 30
# }

To a File

Use -o or --output to write to a file:

jpx -f data.json 'users[?active]' -o active-users.json

jpx -f input.json 'transform(@)' --output result.json

Compact Output

Use -c or --compact for single-line JSON (no pretty-printing):

jpx -c -f data.json 'users[0]'
# {"name":"alice","age":30}

Processing Multiple Files

Evaluate Once Per File

Use --per-file to reuse one jpx process across a corpus. The shell expands globs; jpx evaluates the expression independently for every resulting path:

jpx --per-file --lines 'metadata.title' *.json

# Repeated -f/--file is equivalent
jpx --per-file --lines -f first.json -f second.json 'metadata.title'

Per-file results form one outer array. --lines writes exactly one JSON value per input file; without it, the outer array is rendered normally.

The $file binding contains the path exactly as passed, which makes provenance easy to include in each result:

jpx --per-file --lines '{file: $file, title: metadata.title}' *.json

Combine --per-file with --slurp for JSONL files. Each file is slurped separately before evaluation:

jpx --per-file --slurp --lines \
  -Q sessions.jpx:session-row ~/.local/share/sessions/*.jsonl

--per-file conflicts with --stream and --null-input. The $file binding is a jpx extension and is unavailable in --strict mode.

Shell Loop Alternative

For workflows needing custom per-file control, a shell loop remains useful:

for f in *.json; do
  jpx -f "$f" 'metadata.title'
done

Combining Files with Slurp

Use -s or --slurp to combine multiple JSON values into an array:

cat file1.json file2.json file3.json | jpx -s 'length(@)'
# 3

cat *.json | jpx -s '[*].name'

Streaming JSON Lines (NDJSON)

Use --stream (or --each) for newline-delimited JSON:

# Process each line independently
cat events.ndjson | jpx --stream 'event_type'

# Filter and transform streaming data. Each line is an object, so use if()
# rather than an array filter; null results are skipped
cat logs.ndjson | jpx --stream "if(level == 'error', message, \`null\`)"

Streaming mode uses constant memory regardless of file size. For aggregates across all records (counts, group_by, sorting), use --slurp instead; --stream and --slurp conflict.

Tested recipes live in crates/jpx/tests/fixtures/agent_recipes/recipes.json.

Query Files

Store queries in files for reuse:

Plain Text Query Files

# query.txt contains: users[?active].{name: name, email: email}
jpx -Q query.txt -f data.json

Query Libraries (.jpx)

Create reusable query collections:

-- queries.jpx
-- :name active-users
-- :description Get all active users
users[?active]

-- :name user-emails
-- :description Extract just the email addresses
users[*].email

Run named queries:

jpx -Q queries.jpx:active-users -f data.json

# Or with --query flag
jpx -Q queries.jpx --query user-emails -f data.json

# List available queries
jpx -Q queries.jpx --list-queries

See Query Files for more details.

JSON Patch Operations

Generate Patches

Compare two files and generate a JSON Patch (RFC 6902):

jpx --diff original.json modified.json > changes.patch

Apply Patches

Apply a patch to a document:

jpx --patch changes.patch -f document.json -o updated.json

Merge Patches

Apply a JSON Merge Patch (RFC 7396):

jpx --merge updates.json -f document.json

Tips

Checking File Validity

Use --stats to quickly validate and analyze a JSON file:

jpx --stats -f data.json

Exploring File Structure

Use --paths to see all paths in a JSON document:

jpx --paths -f data.json

# With types
jpx --paths --types -f data.json

# With values
jpx --paths --values -f data.json

Benchmarking File Operations

Measure query performance on a file:

jpx --bench 1000 -f large-data.json 'complex[*].transformation'