17

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"}

NOTE: this question is intentionnaly greatly inspired by it's pretty-print counterpart. However, googling for bash minify json didn't give me a proper result, hence this questions for the minify/uglify.

Ulysse BN
  • 8,503
  • 5
  • 45
  • 74

5 Answers5

27

You can use jq -c (compact) option.

jq -c . < input.json

guizo
  • 2,186
  • 17
  • 25
18

TL;DR: Using jj -u < my.json seems to be the most efficient, using the jj tool.

However, a python one-liner is a quite efficient way if you already have python installed and do not want a new third party tool for such a task:

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

Perf benchmark

Here's the script, using ruby's benchmark-ips:

#!/usr/bin/env ruby
# frozen_string_literal: true

require "benchmark/ips"
require "tempfile"

commands= <<~SH.split("\n")
    python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)'
    jq --compact-output
    xidel -s - -e '$json' --printed-json-format=compact
    jj -u
    yq eval -j -I=0
SH

def label(cmd)
    "%s (%s)" % [
        name = cmd.split.first,
        `#{name} --version 2>&1`[/\d+(\.\d+)*/]
    ]
end

file = Tempfile.new('foo')
file.write <<~JSON
    {
        "foo": "lorem",
        "bar": "ipsum"
    }
JSON
file.close
at_exit { file.unlink }

Benchmark.ips do |x|
    commands.each do |cmd|
        x.report(label(cmd)) do
            system(cmd, in: file.path, out: File::NULL) or raise label(cmd) + " failed"
        end
    end
    x.compare!
end

And the result on my mac (16 GB 2133 MHz LPDDR3, 1.4 GHz Quad-Core Intel Core i5):

Warming up --------------------------------------
     python3 (3.9.6)     2.000  i/100ms
            jq (1.6)     3.000  i/100ms
       xidel (0.9.8)     4.000  i/100ms
          jj (1.2.3)    19.000  i/100ms
         yq (4.11.2)    10.000  i/100ms
Calculating -------------------------------------
     python3 (3.9.6)     23.024  (± 0.0%) i/s -    116.000  in   5.040842s
            jq (1.6)     34.140  (± 2.9%) i/s -    171.000  in   5.011323s
       xidel (0.9.8)     37.127  (±13.5%) i/s -    184.000  in   5.084564s
          jj (1.2.3)    170.997  (±13.5%) i/s -    836.000  in   5.014322s
         yq (4.11.2)     83.604  (±20.3%) i/s -    400.000  in   5.041262s

Comparison:
          jj (1.2.3):      171.0 i/s
         yq (4.11.2):       83.6 i/s - 2.05x  (± 0.00) slower
       xidel (0.9.8):       37.1 i/s - 4.61x  (± 0.00) slower
            jq (1.6):       34.1 i/s - 5.01x  (± 0.00) slower
     python3 (3.9.6):       23.0 i/s - 7.43x  (± 0.00) slower

NOTE: Here is the pretty print benchmark, jj is the best as well!

Ulysse BN
  • 8,503
  • 5
  • 45
  • 74
  • How about `uglify-js`? – GChuf Apr 10 '20 at 15:33
  • If you are talking about this: http://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 :/ – Ulysse BN Apr 10 '20 at 19:40
  • 1
    Example for the python line: `python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < myfile.json` – Grant Foster Jun 08 '20 at 21:46
  • Ah, very nice. Thank you for testing `xidel`. – Reino Sep 07 '20 at 11:05
  • @UlysseBN Could you include/update results for `xidel`'s latest [development build](https://sourceforge.net/projects/videlibri/files/Xidel/Xidel%20development/)? Lots of improvements. Maybe you can also include `xidel` in your [pretty print post](https://stackoverflow.com/a/61119751/6320039)? – Reino Aug 20 '21 at 22:27
  • @Reino I've updated the script for a clearer one, however the latest `xidel` version on brew is only `0.9.8`. – Ulysse BN Aug 21 '21 at 00:28
  • I have no idea what all these numbers (and i/s for instance) mean, so clearer... not for me. That's a real pitty. v0.9.8 is really old. The recommended binaries to use are those from the development branch. – Reino Aug 21 '21 at 16:17
  • @Reino feel free to [contribute to Homebrew](https://github.com/Homebrew/brew#contributing) to add this build! The `i/s` means iterations per seconds, so how many times the script runs in one second. `i/100ms` is the same on a 100ms scale rather than 1s – Ulysse BN Aug 21 '21 at 18:17
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

straville
  • 956
  • 2
  • 14
  • 21
0

With :

xidel -s input.json -e '$json' --printed-json-format=compact
#or
xidel -s input.json -e 'serialize-json($json)'
{"foo": "lorem", "bar": "ipsum"}

Interesting "benchmark", Ulysse BN.
I couldn't test jj, but on my old cpu these are my results:

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

time (for i in {1..100}; do python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' <<< "$var" >& /dev/null; done)

real    0m10.813s
user    0m7.532s
sys     0m5.798s

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

real    0m10.500s
user    0m1.835s
sys     0m0.769s

time (for i in {1..100}; do xidel -se '$json' --printed-json-format=compact <<< "$var" >& /dev/null; done)

real    0m2.250s
user    0m1.692s
sys     0m0.889s
Reino
  • 2,456
  • 1
  • 10
  • 19
0

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"
Sam Bacha
  • 11
  • 1