How to implement custom indentation when pretty-printing with the JSON module?
(Note:
The code in this answer only works with json.dumps()
which returns a JSON formatted string, but not with json.dump()
which writes directly to file-like objects. There's a modified version of it that works with both in my answer to the question Write two-dimensional list to JSON file.)
Updated
Below is a version of my original answer that has been revised several times. Unlike the original, which I posted only to show how to get the first idea in J.F.Sebastian's answer to work, and which like his, returned a non-indented string representation of the object. The latest updated version returns the Python object JSON formatted in isolation.
The keys of each coordinate dict
will appear in sorted order, as per one of the OP's comments, but only if a sort_keys=True
keyword argument is specified in the initial json.dumps()
call driving the process, and it no longer changes the object's type to a string along the way. In other words, the actual type of the "wrapped" object is now maintained.
I think not understanding the original intent of my post resulted in number of folks downvoting it—so, primarily for that reason, I have "fixed" and improved my answer several times. The current version is a hybrid of my original answer coupled with some of the ideas @Erik Allik used in his answer, plus useful feedback from other users shown in the comments below this answer.
The following code appears to work unchanged in both Python 2.7.16 and 3.7.4.
from _ctypes import PyObj_FromPtr
import json
import re
class NoIndent(object):
""" Value wrapper. """
def __init__(self, value):
self.value = value
class MyEncoder(json.JSONEncoder):
FORMAT_SPEC = '@@{}@@'
regex = re.compile(FORMAT_SPEC.format(r'(\d+)'))
def __init__(self, **kwargs):
# Save copy of any keyword argument values needed for use here.
self.__sort_keys = kwargs.get('sort_keys', None)
super(MyEncoder, self).__init__(**kwargs)
def default(self, obj):
return (self.FORMAT_SPEC.format(id(obj)) if isinstance(obj, NoIndent)
else super(MyEncoder, self).default(obj))
def encode(self, obj):
format_spec = self.FORMAT_SPEC # Local var to expedite access.
json_repr = super(MyEncoder, self).encode(obj) # Default JSON.
# Replace any marked-up object ids in the JSON repr with the
# value returned from the json.dumps() of the corresponding
# wrapped Python object.
for match in self.regex.finditer(json_repr):
# see https://stackoverflow.com/a/15012814/355230
id = int(match.group(1))
no_indent = PyObj_FromPtr(id)
json_obj_repr = json.dumps(no_indent.value, sort_keys=self.__sort_keys)
# Replace the matched id string with json formatted representation
# of the corresponding Python object.
json_repr = json_repr.replace(
'"{}"'.format(format_spec.format(id)), json_obj_repr)
return json_repr
if __name__ == '__main__':
from string import ascii_lowercase as letters
data_structure = {
'layer1': {
'layer2': {
'layer3_1': NoIndent([{"x":1,"y":7}, {"x":0,"y":4}, {"x":5,"y":3},
{"x":6,"y":9},
{k: v for v, k in enumerate(letters)}]),
'layer3_2': 'string',
'layer3_3': NoIndent([{"x":2,"y":8,"z":3}, {"x":1,"y":5,"z":4},
{"x":6,"y":9,"z":8}]),
'layer3_4': NoIndent(list(range(20))),
}
}
}
print(json.dumps(data_structure, cls=MyEncoder, sort_keys=True, indent=2))
Output:
{
"layer1": {
"layer2": {
"layer3_1": [{"x": 1, "y": 7}, {"x": 0, "y": 4}, {"x": 5, "y": 3}, {"x": 6, "y": 9}, {"a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, "h": 7, "i": 8, "j": 9, "k": 10, "l": 11, "m": 12, "n": 13, "o": 14, "p": 15, "q": 16, "r": 17, "s": 18, "t": 19, "u": 20, "v": 21, "w": 22, "x": 23, "y": 24, "z": 25}],
"layer3_2": "string",
"layer3_3": [{"x": 2, "y": 8, "z": 3}, {"x": 1, "y": 5, "z": 4}, {"x": 6, "y": 9, "z": 8}],
"layer3_4": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
}
}
}
How to custom indent json dump?
As stated in the comments, it doesn't make functional difference and you will need custom pretty-print. something like
import json
import textwrap
spam = {"a": 1, "b": "2",
"list": [{"c": 3, "d": 4,}]}
eggs = json.dumps(spam, indent=2).splitlines()
eggs = '\n'.join([eggs[0], textwrap.dedent('\n'.join(eggs[1:-1])), eggs[-1]])
print(eggs)
with open('spam.json', 'w') as f:
f.write(eggs)
output
{
"a": 1,
"b": "2",
"list": [
{
"c": 3,
"d": 4
}
]
}
How to prettyprint a JSON file?
Use the indent=
parameter of json.dump()
or json.dumps()
to specify how many spaces to indent by:
>>> import json
>>>
>>> your_json = '["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> parsed = json.loads(your_json)
>>> print(json.dumps(parsed, indent=4))
[
"foo",
{
"bar": [
"baz",
null,
1.0,
2
]
}
]
To parse a file, use json.load()
:
with open('filename.txt', 'r') as handle:
parsed = json.load(handle)
How to pretty print a json.loads method in Python
I think there might be some confusion on what exactly is going on with the json.dumps()
and json.loads()
methods.
# Serializing boy object to a str named boyJSON
# -> resulting str will have JSON format
boyJSON = json.dumps(boy, default=encode_kid, indent=2)
# Serializing boyJSON str to another str boyPYTHON
# -> resulting str will have JSON format, now with str literals
boyPYTHON = json.dumps(boyJSON, indent=2)
That is why boyJSON
and boyPYTHON
will print very differently. If you want them to print the exact same, i.e. "pretty", try this:
boyJSON = json.dumps(boy, default=encode_kid, indent=2)
boyPYTHON = json.loads(boyJSON) # Converts boyJSON str to a dict
print(boyJSON)
print(json.dumps(boyPYTHON, indent=2)) # Print boyPYTHON dict converted to a str with JSON format
Output:
>>> print(boyJSON)
{
"name": "Jimmy",
"age": 10,
"nickname": null,
"Is friendly": false
}
>>> print(json.dumps(boyPYTHON, indent=2))
{
"name": "Jimmy",
"age": 10,
"nickname": null,
"Is friendly": false
}
>>> print(boyPYTHON)
{'name': 'Jimmy', 'age': 10, 'nickname': None, 'Is friendly': False}
>>> from pprint import pprint
>>> pprint(boyPYTHON, width=1)
{'Is friendly': False,
'age': 10,
'name': 'Jimmy',
'nickname': None}
Dumping a JSON using tab indents (not spaces)
Python 2.7
there is a workaround that can be implemented using regular expressions :
import re
dump = json.dumps(data, sort_keys=True, indent=4, separators=(',', ': '))
#Replaces spaces with tab
new_data = re.sub('\n +', lambda match: '\n' + '\t' * (len(match.group().strip('\n')) / 3), dump)
json.dump(new_data, open('dev_integrated.json', 'w')
Python 3.2+
From the Docs :
If indent is a non-negative integer or string, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0, negative, or "" will only insert newlines. None (the default) selects the most compact representation. Using a positive integer indent indents that many spaces per level. If indent is a string (such as "\t"), that string is used to indent each level.
Hence the TAB-indentation can be implemented as follows:
json.dump(jString, open('dev_integrated.json', 'w'), sort_keys=True, indent='\t', separators=(',', ': '))
Pretty-Print JSON Data to a File using Python
You should use the optional argument indent
.
header, output = client.request(twitterRequest, method="GET", body=None,
headers=None, force_auth_header=True)
# now write output to a file
twitterDataFile = open("twitterData.json", "w")
# magic happens here to make it pretty-printed
twitterDataFile.write(simplejson.dumps(simplejson.loads(output), indent=4, sort_keys=True))
twitterDataFile.close()
How to pretty print nested dictionaries?
I'm not sure how exactly you want the formatting to look like, but you could start with a function like this:
def pretty(d, indent=0):
for key, value in d.items():
print('\t' * indent + str(key))
if isinstance(value, dict):
pretty(value, indent+1)
else:
print('\t' * (indent+1) + str(value))
Related Topics
Detect & Record Audio in Python
How to Make Setuptools Install a Package That's Not on Pypi
How to Apply a Disc Shaped Mask to a Numpy Array
Check If Any Alert Exists Using Selenium with Python
The Difference Between Sys.Stdout.Write and Print
How to Send an Email with Python
How to Map Numeric Data into Categories/Bins in Pandas Dataframe
Popen Waiting for Child Process Even When the Immediate Child Has Terminated
How to Implement Custom Indentation When Pretty-Printing with the JSON Module
How to Install Python Opencv Through Conda
Why Is "If Not Someobj:" Better Than "If Someobj == None:" in Python
In Python Interpreter, Return Without " ' "