Search code examples
jsonpretty-print

How do I pretty print JSON with multiple levels of minimization?


We have standard pretty printed JSON:

{
  "results": {
    "groups": {
      "alpha": {
        "items": {
          "apple": {
            "attributes": {
              "class": "fruit"
            }
          },
          "pear": {
            "attributes": {
              "class": "fruit"
            }
          },
          "dog": {
            "attributes": {
              "class": null
            }
          }
        }
      },
      "beta": {
        "items": {
          "banana": {
            "attributes": {
              "class": "fruit"
            }
          }
        }
      }
    }
  }
}

And we have JMin:

{"results":{"groups":{"alpha":{"items":{"apple":{"attributes":{"class":"fruit"}},"pear":{"attributes":{"class":"fruit"}},"dog":{"attributes":{"class":null}}}},"beta":{"items":{"banana":{"attributes":{"class":"fruit"}}}}}}}

But I want to be able to print JSON like this on the fly:

{
  "results" : {
    "groups" : {
      "alpha" : {
        "items" : {
          "apple":{"attributes":{"class":"fruit"}},
          "pear":{"attributes":{"class":"fruit"}},
          "dog":{"attributes":{"class":null}}
        }
      },
      "beta" : {
        "items" : {
          "banana":{"attributes":{"class":"fruit"}}}
      }
    }
  }
}

The above I would describe as "pretty-print JSON, minimized at level 5". Are there any tools that do that?


Solution

  • I wrote my own JSON formatter, based on this script:

    #! /usr/bin/env python
    VERSION = "1.0.1"
    
    import sys
    import json
    from optparse import OptionParser
    
    def to_json(o, level=0):
        if level < FOLD_LEVEL:
            newline = "\n"
            space = " "
        else:
            newline = ""
            space = ""
        ret = ""
        if isinstance(o, basestring):
            o = o.encode('unicode_escape')
            ret += '"' + o + '"'
        elif isinstance(o, bool):
            ret += "true" if o else "false"
        elif isinstance(o, float):
            ret += '%.7g' % o
        elif isinstance(o, int):
            ret += str(o)
        elif isinstance(o, list):
            #ret += "[" + ",".join([to_json(e, level+1) for e in o]) + "]"
            ret += "[" + newline
            comma = ""
            for e in o:
                ret += comma
                comma = "," + newline
                ret += space * INDENT * (level+1)
                ret += to_json(e, level+1)
            ret += newline + space * INDENT * level + "]"
        elif isinstance(o, dict):
            ret += "{" + newline
            comma = ""
            for k,v in o.iteritems():
                ret += comma
                comma = "," + newline
                ret += space * INDENT * (level+1)
                #ret += '"' + str(k) + '"' + space + ':' + space
                ret += '"' + str(k) + '":' + space
                ret += to_json(v, level+1)
            ret += newline + space * INDENT * level + "}"
        elif o is None:
            ret += "null"
        else:
            #raise TypeError("Unknown type '%s' for json serialization" % str(type(o)))
            ret += str(o)
        return ret
    
    
    #main():
    FOLD_LEVEL = 10000
    INDENT = 4
    
    parser = OptionParser(usage='%prog json_file [options]', version=VERSION)
    parser.add_option("-f", "--fold-level", action="store", type="int",
              dest="fold_level", help="int (all json is minimized to this level)")
    parser.add_option("-i", "--indent", action="store", type="int",
              dest="indent", help="int (spaces of indentation, default is 4)")
    parser.add_option("-o", "--outfile", action="store", type="string",
              dest="outfile", metavar="filename", help="write output to a file")
    (options, args) = parser.parse_args()
    
    if len(args) == 0:
        infile = sys.stdin
    elif len(args) == 1:
        infile = open(args[0], 'rb')
    else:
        raise SystemExit(sys.argv[0] + " json_file [options]")
    if options.outfile == None:
        outfile = sys.stdout
    else:
        outfile = open(options.outfile, 'wb')
    if options.fold_level != None:
        FOLD_LEVEL = options.fold_level
    if options.indent != None:
        INDENT = options.indent
    
    with infile:
        try:
            obj = json.load(infile)
        except ValueError, e:
            raise SystemExit(e)
    with outfile:
        outfile.write(to_json(obj))
        outfile.write('\n')
    

    The script accepts fold level, indent and output file from the command line:

    $ jsonfold.py -h
    Usage: jsonfold.py json_file [options]
    
    Options:
      --version             show program's version number and exit
      -h, --help            show this help message and exit
      -f FOLD_LEVEL, --fold-level=FOLD_LEVEL
                            int (all json is minimized to this level)
      -i INDENT, --indent=INDENT
                            int (spaces of indentation, default is 4)
      -o filename, --outfile=filename
                            write output to a file
    

    To get my example from above, fold at the 5th level:

    $ jsonfold.py test2 -f 5
    {
        "results": {
            "groups": {
                "alpha": {
                    "items": {
                        "pear": {"attributes":{"class":"fruit"}},
                        "apple": {"attributes":{"class":"fruit"}},
                        "dog": {"attributes":{"class":None}}
                    }
                },
                "beta": {
                    "items": {
                        "banana": {"attributes":{"class":"fruit"}}
                    }
                }
            }
        }
    }