I have a large dictionary that has some large array data in it:
d = {'something': {'else': 'x'}, 'longnumbers': [1,2,3,4,54,6,67,7,7,8,8,8,6,4,3,3,5,6,7,4,3,5,6,54]}
The real dictionary has many more keys and a nested structure. When I use json.dump
without indent
, I get a compact, single-line output which is not readable. When I set indent
, it puts newlines after every separator, including the arrays.
The numerical arrays are long and end up like this:
"longnumbers": [
1,
2,
3,
4,
54,
6,
67,
7,
7,
8,
8,
8,
6,
4,
3,
3,
5,
6,
7,
4,
3,
5,
6,
54
],
Is there any way to get pretty-printed JSON with an indent level, but without placing newlines after array elements? For the example above, I'd like something like this:
{
"longnumbers": [1, 2, 3, 4, 54, 6, 67, 7, 7, 8, 8, 8, 6, 4, 3, 3, 5, 6, 7, 4, 3, 5, 6, 54],
"something": {
"else": "x"
}
}
I ended up just writing my own JSON serializer:
import numpy
INDENT = 3
SPACE = " "
NEWLINE = "\n"
def to_json(o, level=0):
ret = ""
if isinstance(o, dict):
ret += "{" + NEWLINE
comma = ""
for k,v in o.iteritems():
ret += comma
comma = ",\n"
ret += SPACE * INDENT * (level+1)
ret += '"' + str(k) + '":' + SPACE
ret += to_json(v, level + 1)
ret += NEWLINE + SPACE * INDENT * level + "}"
elif isinstance(o, basestring):
ret += '"' + o + '"'
elif isinstance(o, list):
ret += "[" + ",".join([to_json(e, level+1) for e in o]) + "]"
elif isinstance(o, bool):
ret += "true" if o else "false"
elif isinstance(o, int):
ret += str(o)
elif isinstance(o, float):
ret += '%.7g' % o
elif isinstance(o, numpy.ndarray) and numpy.issubdtype(o.dtype, numpy.integer):
ret += "[" + ','.join(map(str, o.flatten().tolist())) + "]"
elif isinstance(o, numpy.ndarray) and numpy.issubdtype(o.dtype, numpy.inexact):
ret += "[" + ','.join(map(lambda x: '%.7g' % x, o.flatten().tolist())) + "]"
elif o is None:
ret += 'null'
else:
raise TypeError("Unknown type '%s' for json serialization" % str(type(o)))
return ret
Ugh, should really be an option for specifying different indents for the two different JSON container types by now. An alternative approach if you want to stay compatible with the core Python JSON lib is to override the function (_make_iterencode()
currently) in that lib that is responsible for handling indent
.
Had a crack at reimplementation of _make_iterencode(). Only had to change a few lines to make the indent
option, optionally take a tuple (hash-indent, array-indent)
. But unfortunately have to replace an entire _make_iterencode()
which turns out to be pretty big and poorly decomposed. Anyway, following works for 3.4-3.6:
import sys
import json
dat = {"b": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "a": 1, "c": "x"}
indent = 2
print(json.dumps(dat, indent=indent))
if sys.version_info.major == 3 and 4 <= sys.version_info.minor <= 6:
import _make_iterencode
json.encoder._make_iterencode = _make_iterencode._make_iterencode
indent = (2, None)
print(json.dumps(dat, indent=indent))
Gives:
{
"c": "x",
"a": 1,
"b": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
]
}
{
"c": "x",
"a": 1,
"b": [1,2,3,4,5,6,7,8,9,10]
}