| name | jq |
| description | JSON processing, parsing, and manipulation. STRONGLY PREFERRED for all JSON formatting, filtering, transformations, and analysis. Use instead of Python/Node.js scripts for JSON operations. |
jq - JSON Processing
IMPORTANT: jq is the STRONGLY PREFERRED tool for ALL JSON formatting, parsing, manipulation, and analysis tasks. Use jq instead of Python/Node.js scripts, grep, awk, or other text processing tools when working with JSON data.
Core Philosophy
- Always use jq for JSON: If the data is JSON or can be converted to JSON, use jq
- Streaming-friendly: jq processes JSON as a stream, making it memory-efficient for large files
- Composable: jq filters can be chained with pipes, just like shell commands
- Pure and functional: jq transformations are predictable and side-effect-free
Basic Usage Patterns
Pretty-Printing and Formatting
cat file.json | jq '.'
jq '.' file.json
jq -c '.' file.json
jq -S '.' file.json
jq -r '.field' file.json
jq -n '{name: "value", count: 42}'
Selecting and Filtering
jq '.field' file.json
jq '.user.email' file.json
jq '.[0]' array.json
jq '.[2:5]' array.json
jq '{name: .name, email: .email}' file.json
jq '.[] | select(.age > 21)' users.json
jq '.[] | select(.active == true and .role == "admin")' users.json
jq '.[] | select(.price < 100) | {name, price}' products.json
Array Operations
jq 'map(.name)' users.json
jq '[.[] | .email]' users.json
jq 'map(select(.active)) | map(.name)' users.json
jq 'length' array.json
jq '.items | length' file.json
jq 'sort' numbers.json
jq 'sort_by(.created_at)' items.json
jq 'reverse' array.json
jq 'unique' array.json
jq 'unique_by(.category)' items.json
jq 'group_by(.category)' items.json
jq 'flatten' nested.json
jq 'flatten(2)' deeply_nested.json
Aggregations and Statistics
jq 'map(.price) | add' items.json
jq '[.[] | .count] | add' data.json
jq 'map(.score) | add / length' scores.json
jq 'map(.price) | min' products.json
jq 'map(.price) | max' products.json
jq 'min_by(.created_at)' items.json
jq 'max_by(.score)' results.json
jq 'group_by(.status) | map({status: .[0].status, count: length})' items.json
Transforming Data
jq '. + {new_field: "value"}' file.json
jq '.price = .price * 1.1' product.json
jq '.updated_at = now' record.json
jq '{name: .old_name, other: .other}' file.json
jq 'del(.sensitive_data)' file.json
jq 'if .price > 100 then .category = "premium" else . end' product.json
jq 'map(. + {full_name: "\(.first_name) \(.last_name)"})' users.json
Combining and Merging
jq '. + {extra: "data"}' file.json
jq '. * {override: "value"}' file.json
jq '. + [1,2,3]' array.json
jq -s '.[0] + .[1]' file1.json file2.json
jq -s '.' file1.json file2.json file3.json
jq -s 'map(.items) | flatten' *.json
Working with Keys
jq 'keys' object.json
jq 'keys_unsorted' object.json
jq 'has("field")' file.json
jq 'values' object.json
jq '.[] | values' array.json
jq 'to_entries' object.json
jq 'to_entries | map({key: .key, value: .value})' object.json
jq 'from_entries' pairs.json
String Operations
jq '"\(.first_name) \(.last_name)"' user.json
jq '.name | ascii_downcase' file.json
jq '.email | ascii_upcase' file.json
jq '.text | ltrimstr("prefix:")' file.json
jq '.url | rtrimstr(".html")' file.json
jq '.tags | split(",")' file.json
jq '.words | join(" ")' file.json
jq '.email | test("@example\\.com$")' user.json
jq '.text | match("\\b\\w+@\\w+\\.\\w+\\b")' content.json
jq '.name | sub("old"; "new")' file.json
jq '.text | gsub("\\s+"; " ")' file.json
Conditional Logic
jq 'if .age >= 18 then "adult" else "minor" end' user.json
jq 'if .score > 90 then "A" elif .score > 80 then "B" else "C" end' result.json
jq '.optional_field // "default"' file.json
jq 'try .field.nested catch "not found"' file.json
Type Conversions
jq 'tostring' number.json
jq 'tonumber' string.json
jq 'type' value.json
jq '.[] | select(type == "number")' mixed.json
jq 'if type == "array" then length else 1 end' value.json
Output Formatting
jq -r '.[] | [.name, .email, .age] | @tsv' users.json
jq -r '.[] | [.name, .email, .age] | @csv' users.json
jq -r '@uri' string.json
jq -r '@base64' string.json
jq -r '@base64d' encoded.json
jq -r '@html' string.json
jq -r '@sh' command.json
Advanced Patterns
jq '.. | .id? | select(. != null)' nested.json
jq 'walk(if type == "string" then ascii_downcase else . end)' file.json
jq 'path(.items[].name)' file.json
jq 'getpath(["items", 0, "name"])' file.json
jq 'limit(5; .[] | select(.active))' items.json
jq 'until(. > 100; . * 2)' number.json
jq 'reduce .[] as $item (0; . + $item.count)' items.json
jq 'reduce .[] as $item ({}; . + {($item.key): $item.value})' pairs.json
Working with API Responses
curl -s api.example.com/data | jq '.'
curl -s api.example.com/users | jq '.[] | {id, name, email}'
curl -s api.example.com/items | jq 'map(select(.active)) | sort_by(.created_at) | reverse | .[0:10]'
for page in {1..5}; do
curl -s "api.example.com/items?page=$page" | jq '.items[]'
done | jq -s '.'
curl -s api.example.com/data | jq 'if .error then .error.message else .data end'
Log Analysis
cat app.log | jq -R 'fromjson? | select(.level == "error")'
cat app.log | jq -R 'fromjson? | select(.level == "error")' | jq -s 'group_by(.error_type) | map({type: .[0].error_type, count: length})'
cat app.log | jq -R 'fromjson? | select(.timestamp > "2024-01-01")'
cat app.log | jq -R 'fromjson? | select(.stack_trace) | .stack_trace'
Multi-File Processing
jq -s 'add' file1.json file2.json file3.json
jq -s 'map(.)' *.json
jq -s 'map(.items) | flatten | group_by(.category)' *.json
jq -s '.[0].users as $users | .[1].orders | map(. + {user: ($users[] | select(.id == .user_id))})' users.json orders.json
Performance Optimization
jq --stream 'select(length == 2)' huge.json
cat data.ndjson | jq -c '.'
jq -c '.' file.json > compressed.json
cat stream.json | jq -c 'select(.important)' >> filtered.json
Debugging
jq --debug '.field' file.json
jq 'debug | .field' file.json
jq '. as $orig | .field | debug | $orig' file.json
jq empty file.json
jq 'paths(. == null)' file.json
Common Patterns for Claude Code Tasks
Extract Configuration Values
jq -r '.database.host' config.json
jq -r '.env | to_entries | .[] | "\(.key)=\(.value)"' config.json
Process API Responses
gh api /repos/owner/repo/issues | jq '.[].number'
gh api /repos/owner/repo/pulls | jq -r '.[] | "\(.number): \(.title)"'
Transform Test Results
cat test-results.json | jq '{total: .stats.tests, passed: .stats.passes, failed: .stats.failures}'
cat test-results.json | jq '.tests[] | select(.state == "failed") | .title'
Modify Package Files
jq '.version = "2.0.0"' package.json > package.json.tmp && mv package.json.tmp package.json
jq '.dependencies["new-package"] = "^1.0.0"' package.json > package.json.tmp && mv package.json.tmp package.json
jq 'del(.devDependencies["old-package"])' package.json > package.json.tmp && mv package.json.tmp package.json
When NOT to Use jq
- Binary data: jq is for text-based JSON only
- Very simple extraction: For trivial field access where grep would suffice
- Modifying files in place: jq doesn't support in-place editing (use temp files)
- Non-JSON data: Use appropriate tools (xsv for CSV, yq for YAML, etc.)
Error Messages and Troubleshooting
Common Errors
Validation
jq empty file.json && echo "Valid JSON" || echo "Invalid JSON"
jq '.' file.json
Performance Considerations
- Use
-c for compact output when piping to other commands
- Stream large files with
--stream or process line-by-line for NDJSON
- Filter early to reduce data size before complex transformations
- Avoid unnecessary array creation - use streaming operations when possible
- Use built-in functions like
map, select, group_by instead of manual loops
Integration with Other Tools
curl -s api.example.com/data | jq '.results[]'
cat logs.ndjson | grep "error" | jq '.'
cat data.json | jq -r '.[] | @tsv' | awk -F'\t' '{print $1, $3}'
jq -r '.files[]' list.json | xargs -I {} cp {} dest/
cat items.json | jq -c '.[]' | parallel -j4 'echo {} | jq ".id"'
Resources