68

I've been looking for a way to uglify some JSON while in my bash console. This help using it afterward in another command (for example, to pass json inline to httpie).

Giving:

{
    "foo": "lorem",
    "bar": "ipsum"
}

I want to obtain:

{"foo":"lorem","bar":"ipsum"}

See the pretty-print counterpart.

4
  • suggestion: add some white-space to the sample strings here. Commented Aug 30, 2023 at 15:56
  • I'm not sure about what you are meaning @orionelenzil, but i you mean strings with spaces inside (" lorem. "), I'm not expecting those to be compacted afterward. This question is just an example btw, you'll see that the answer also use an external, way larger json Commented Aug 30, 2023 at 19:56
  • right, it's a minor thing but with your existing example naively removing whitespace would work fine, but if there were whitespace in the values or keys then such a simple approach would not work, thus motivating the use of something fancier like the suggestions in the answers. Commented Aug 30, 2023 at 20:40
  • Sorry, I don't seem to get what you want... I'd rather keep it simple anyway, this is not a use case, just an example! Commented Aug 30, 2023 at 20:53

5 Answers 5

93

You can use jq -c (compact) option.

jq -c . < input.json

Sign up to request clarification or add additional context in comments.

4 Comments

Doesn't work if you have a deeply nested JSON file. See: stackoverflow.com/questions/75524357/…
In fact, the recursion limit of jq seems to be 512, see stackoverflow.com/a/75524782/7483211 and based on benchmarks jj is much faster!
cat input.json | jq -c > output.json is less awkward and more handy for huge files
All you've done there is add an unnecessary cat
39

TL;DR

no install

python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < my.json

very fast (with jj)

jj -u < my.json

Perf benchmark

Here's the script, using hyperfine:

#!/usr/bin/env bash

tmp=$(mktemp json.XXX)
tmp_md=$(mktemp md.XXX)

trap "rm $tmp $tmp_md" EXIT

cat <<JSON > $tmp
{
    "foo": "lorem",
    "bar": "ipsum"
}
JSON
hyperfine \
    --export-markdown $tmp_md \
    --warmup 100 \
    "jj -u < $tmp" \
    "yq eval -j -I=0 < $tmp" \
    "xidel -s - -e '\$json' --printed-json-format=compact < $tmp" \
    "jq --compact-output < $tmp" \
    "python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < $tmp" \
    "ruby -r json -e 'j JSON.parse \$stdin.read' < $tmp"

pbcopy < $tmp_md

The result on my mac — MacBook Air (M1, 2020), 8 GB:

Command Mean [ms] Min [ms] Max [ms] Relative
jj -u < json.p72 1.3 ± 0.2 0.9 2.7 1.00
yq eval -j -I=0 < json.p72 4.4 ± 0.4 3.8 7.8 3.37 ± 0.65
xidel -s - -e '$json' --printed-json-format=compact < json.p72 5.5 ± 0.3 5.0 6.5 4.19 ± 0.77
python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < json.p72 14.0 ± 0.4 13.4 15.0 10.71 ± 1.89
jq --compact-output < json.p72 14.4 ± 2.0 13.2 33.6 11.02 ± 2.45
ruby -r json -e 'j JSON.parse $stdin.read' < json.p72 47.3 ± 0.6 46.1 48.5 36.10 ± 6.32

Result for a large JSON file (14k lines):

http https://france-geojson.gregoiredavid.fr/repo/regions.geojson | jj -p > $tmp
Command Mean [ms] Min [ms] Max [ms] Relative
jj -u < json.wFY 3.4 ± 0.7 2.7 12.2 1.00
jq --compact-output < json.wFY 35.1 ± 0.4 34.5 36.1 10.24 ± 2.23
python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < json.wFY 47.4 ± 0.5 46.3 48.7 13.82 ± 3.01
xidel -s - -e '$json' --printed-json-format=compact < json.wFY 55.5 ± 1.2 54.7 63.5 16.17 ± 3.53
ruby -r json -e 'j JSON.parse $stdin.read' < json.wFY 94.9 ± 0.7 93.8 96.8 27.65 ± 6.02
yq eval -j -I=0 < json.wFY 3087.0 ± 26.6 3049.3 3126.8 899.63 ± 195.81

And here is the pretty print counterpart benchmark

10 Comments

How about uglify-js?
If you are talking about this: lisperator.net/uglifyjs, it is a javascript uglifier, not JSON. And node is less often available than python on computers. Hence I'm not sure it is a good candidate :/
Example for the python line: python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < myfile.json
@UlysseBN Could you include/update results for xidel's latest development build? Lots of improvements. Maybe you can also include xidel in your pretty print post?
the 'no install' python command-line here produces not-fully-compacted JSON for me. it has spaces after : and ,'s. jq -c omits those spaces.
|
1

yq worked for me, via utilization of input file (containing the prettified JSON)
yq eval -j -I=0 uglify-test.txt
Docs link: https://mikefarah.gitbook.io/yq/usage/convert

1 Comment

I've added it to the benchmark! It seems surprisingly faster than jq !
0

With the JSON-parser and command line tool :

$ xidel -s "input.json" -e '$json' --output-json-indent=compact
{"foo": "lorem", "bar": "ipsum"}

$ xidel -s "input.json" -e 'serialize($json,{"method":"json"})'
{"foo":"lorem","bar":"ipsum"}

Interesting "benchmark", @Ulysse BN. These are my results on an AMD Ryzen 3 3200U mobile cpu:

$ var='{
    "foo": "lorem",
    "bar": "ipsum"
}'

$ jq --version
jq-1.7
$ time (for i in {1..1000}; do jq --compact-output <<< "$var" >& /dev/null; done)

real    0m3.640s
user    0m0.862s
sys     0m0.686s

$ xidel --version
xidel 0.9.9 2025-02-22-git-3e7e88a62307
[...]
$ time (for i in {1..1000}; do xidel -se '$json' --output-json-indent=compact <<< "$var" >& /dev/null; done)

real    0m3.545s
user    0m0.958s
sys     0m0.709s

$ jj --version
jj - JSON Stream Editor 1.9.2
$ time (for i in {1..1000}; do jj -u <<< "$var" >& /dev/null; done)

real    0m2.453s
user    0m1.903s
sys     0m0.721s

1 Comment

Very interesting solution! I've added it to my benchmark to have an answer easier to read :)
-2

jq-minify

Here is a bash script that will write back to the file minified

works with bash v3.2+ and jq v1.6+

#!/usr/bin/env bash
set -eu
path=
options=()
# change -c to -r to get pretty-print
set -- "$@" -c .
for arg; do
  if [ -f "$arg" ]; then
    if [ -n "$path" ]; then
      echo "Cannot specify multiple paths to jq-minify" >&2
      exit 1
    fi
    path="$arg"
  else
    options+=("$arg")
  fi
done
tmp=$(mktemp)
jq "${options[@]}" "$path" >"$tmp"
cat "$tmp" >"$path"

2 Comments

I'm not sure I see this scripts point, why not jq -c file > file1 ?
It creates a temporary file to safely transform the JSON before overwriting the original file with the processed version. As noted in the comments above, the command will error on deeply nested JSON objects. Also it prevents errors, like in your example: jq -c file > file1 is invalid, the correct command is jq -c . file > file1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.