(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]
    }
  }
}
Answer from martineau on Stack Overflow
Top answer
1 of 16
32

(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]
    }
  }
}
2 of 16
18

A bodge, but once you have the string from dumps(), you can perform a regular expression substitution on it, if you're sure of the format of its contents. Something along the lines of:

s = json.dumps(data_structure, indent=2)
s = re.sub('\s*{\s*"(.)": (\d+),\s*"(.)": (\d+)\s*}(,?)\s*', r'{"\1":\2,"\3":\4}\5', s)
🌐
Python
docs.python.org › 3 › library › json.html
json — JSON encoder and decoder
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 ...
🌐
Vertabelo Academy
academy.vertabelo.com › course › python-json › writing-json-files › writing-to-json-file › jsondumps-options-the-indent
How to Read and Write JSON Files in Python | Learn Python | Vertabelo Academy
A positive integer indent indent ... An indent level of 0 or negative will only insert newlines. None (the default) selects the most compact representation. Let's have a look at the result.
🌐
Pradet
quentin.pradet.me › blog › indenting-json-in-python.html
Indenting JSON in Python
def output_unescaped_json(value, *, indent=0): out = '' next_indent = indent + 2 if isinstance(value, int): out += str(value) elif isinstance(value, list): # opening [ and indentation until first item out += '[\n' + ' ' * next_indent # each item separated by commas and indentation out_items = [ output_unescaped_json(item, indent=next_indent) for item in value ] sep = ',\n' + ' ' * next_indent out += sep.join(out_items) # indentation between the last item and ] out += '\n' + ' ' * indent + ']' else: assert False, type(value) return out · As with textbook recursive algorithms, it is elegant because you're only solving one problem at a time, but it can be tricky to get to the solution, convince you that it works, and debug it.
🌐
GitHub
gist.github.com › jannismain › e96666ca4f059c3e5bc28abb711b5c92
A JSON Encoder in Python, that puts small lists on single lines. · GitHub
$ python3 CompactJSONEncoder.py --example { "compact_object": { "first": "element", "second": 2 }, "compact_list": ["first", "second"], "long_list": [ "this", "is", "a", "rather", "long", "list" ] } ... @keithzg Ahh, I see what the problem is: json.dump works with indent parameters given as int and str, while my encoder assumes and int to multiply my indentation level with. I have reworked it so indent can be provided both as int and str. Providing indent="\t" works as expected now. ... Not only newlines need to be escaped, I think return json.dumps(o) is better.
🌐
EyeHunts
tutorial.eyehunts.com › home › python json dumps indent | example
Python JSON dumps indent | Example
December 7, 2022 - A simple example code uses the indent parameter of json. dump() to specify the indentation value. Pretty-Printed JSON data into a file with indent=4 · import json data = '[{"ID":101,"Name":"John","Class":"First"},' \ '{"ID":102,"Name":"Tim","Class":"Second"}]' res = json.loads(data) # Indent = 3 print(json.dumps(res, indent=4))
🌐
GeeksforGeeks
geeksforgeeks.org › json-dumps-in-python
json.dumps() in Python - GeeksforGeeks
import json Dictionary ={(1, 2, 3):'Welcome', 2:'to', 3:'Geeks', 4:'for', 5:'Geeks', 6:float('nan')} # If specified, separators should be # an (item_separator, key_separator)tuple # Items are separated by '.' and key, # values are separated by '=' json_string = json.dumps(Dictionary, skipkeys = True, allow_nan = True, indent = 6, separators =(".
Published   June 19, 2024
🌐
Reddit
reddit.com › r/learnpython › formatting json output in python
r/learnpython on Reddit: formatting json output in Python
May 12, 2022 -

Hi,

I would like to read json into Python code, and then output processed json. In order to get started with this, I have written very basic Python, and am attempting to read in very basic json I found online.

The input json is:

{
    "firstName": "John",
    "lastName": "Doe",
    "hobbies": ["biking", "coding", "rapping"],
    "age": 35,
    "children": [
        {
            "firstName": "hector",
            "age": 6
        },
        {
            "firstName": "cassandra",
            "age": 8
        }
    ]
}

The code is:

import json

if __name__ == '__main__':
    
    print( "start" )

    # read and load input json
    json_input_filename = "input.json"
    json_input = open( json_input_filename )

    json_input_dict = json.load( json_input )

    # write output json
    json_output_filename = "output.json"
    with open( json_output_filename, 'w' ) as json_output:
        json.dump( json_string, json_output )
  

    print( f"end" )

and the output is:

"{\"firstName\": \"John\", \"lastName\": \"Doe\", \"hobbies\": [\"biking\", \"coding\", \"rapping\"], \"age\": 35, \"children\": [{\"firstName\": \"hector\", \"age\": 6}, {\"firstName\": \"cassandra\", \"age\": 8}]}"

What can I do in order to preserve something resembling the original formatting? I'm going to load this output into some other code in order to process it further.

Thank you very much

Find elsewhere
🌐
DigitalOcean
digitalocean.com › community › tutorials › python-pretty-print-json
How to Pretty Print JSON in Python | DigitalOcean
September 16, 2025 - Use indent and sort_keys in json.dumps() to instantly make your JSON output readable and consistently structured.
🌐
PYnative
pynative.com › home › python › json › python prettyprint json data
Python PrettyPrint JSON Data
May 14, 2021 - If indent is a non-negative integer ... we specify indent=4, Python uses four spaces for indentation. An indent level of 0, negative, or "" will only insert newlines....
🌐
JSON for Modern C++
json.nlohmann.me › api › basic_json › dump
dump - JSON for Modern C++
The function tries to mimic Python's json.dumps() function, and currently supports its indent and ensure_ascii parameters. ... If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines.
🌐
Towards Data Science
towardsdatascience.com › home › latest › you must know python json dumps, but maybe not all aspects
You Must Know Python JSON Dumps, But Maybe Not All Aspects | Towards Data Science
January 20, 2025 - Since these two methods have the same signatures, this article will be only focused on the dumps(), because all these tricks will work for the other one. See that string output by the dumps() method? It is not ideal for reading. If we have a really large JSON document, everything will be output in a single line. In order to output the JSON string with the pretty format, we can easily add a parameter "indent".
🌐
ReqBin
reqbin.com › code › python › 0l6wsqxp › python-pretty-print-json-example
How do I pretty print JSON in Python?
The json.dumps() method takes a ... the JSON string. If the json.dumps() indentation parameter is negative 0, or an empty string, then there is no indentation, and only newlines are inserted....
🌐
iProyal
iproyal.com › blog › json-dump-python
How to Use json.dump() in Python: A Beginner-Friendly Guide
May 19, 2025 - When saving a Python dictionary or list, always use json.dump Python with indent=4. This will make your JSON file readable. It’s a small thing, but it makes a big difference when you open the JSON file later or give it to someone else. Always make sure your Python object only has simple types inside it.
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › JSON › stringify
JSON.stringify() - JavaScript | MDN
Each level of indentation will never be longer than 10. Number values of space are clamped to 10, and string values are truncated to 10 characters. ... JSON.stringify({}); // '{}' JSON.stringify(true); // 'true' JSON.stringify("foo"); // '"foo"' JSON.stringify([1, "false", false]); // ...
🌐
Real Python
realpython.com › python-json
Working With JSON Data in Python – Real Python
August 20, 2025 - As you learned before, json.dumps needs JSON data as the first argument and then accepts a value for the indentation. The default value for indent is None, so you could skip setting the argument explicitly like you do above.