I'd like to dump a Python dictionary into a JSON file with a particular custom format. For example, the following dictionary my_dict
,
'text_lines': [{"line1"}, {"line2"}]
dumped with
f.write(json.dumps(my_dict, sort_keys=True, indent=2))
looks like this
"text_lines": [ { "line1" }, { "line2" } ]
while I prefer that it looks like this
"text_lines": [ {"line1"}, {"line2"} ]
Similarly, I want the following
"location": [ 22, -8 ]
to look like this
"location": [22, -8]
(that is, more like a coordinate, which it is).
I know that this is a cosmetic issue, but it's important to me to preserve this formatting for easier hand editing of the file.
Any way of doing this kind of customisation? An explained example would be great (the docs did not get me very far).
json. dump() method used to write Python serialized object as JSON formatted data into a file. json. dumps() method is used to encodes any Python object into JSON formatted String.
The dump() method is used when the Python objects have to be stored in a file. The dumps() is used when the objects are required to be in string format and is used for parsing, printing, etc, . The dump() needs the json file name in which the output has to be stored as an argument.
dumps() is for converting to JSON, not from JSON to string. str has absolutely nothing to do with JSON; the fact that str(somedict) looks sort of like JSON is coincidence.
The indent parameter specifies the spaces that are used at the beginning of a line. We can use the indent parameter of json. dump() to specify the indentation value. By default, when you write JSON data into a file, Python doesn't use indentations and writes all data on a single line, which is not readable.
I have used the example provided by Tim Ludwinski and adapted it to my preference:
class CompactJSONEncoder(json.JSONEncoder): """A JSON Encoder that puts small lists on single lines.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.indentation_level = 0 def encode(self, o): """Encode JSON object *o* with respect to single line lists.""" if isinstance(o, (list, tuple)): if self._is_single_line_list(o): return "[" + ", ".join(json.dumps(el) for el in o) + "]" else: self.indentation_level += 1 output = [self.indent_str + self.encode(el) for el in o] self.indentation_level -= 1 return "[\n" + ",\n".join(output) + "\n" + self.indent_str + "]" elif isinstance(o, dict): self.indentation_level += 1 output = [self.indent_str + f"{json.dumps(k)}: {self.encode(v)}" for k, v in o.items()] self.indentation_level -= 1 return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}" else: return json.dumps(o) def _is_single_line_list(self, o): if isinstance(o, (list, tuple)): return not any(isinstance(el, (list, tuple, dict)) for el in o)\ and len(o) <= 2\ and len(str(o)) - 2 <= 60 @property def indent_str(self) -> str: return " " * self.indentation_level * self.indent def iterencode(self, o, **kwargs): """Required to also work with `json.dump`.""" return self.encode(o)
Also see the version I have in use.
Here's something that I hacked together. Not very pretty but it seems to work. You could probably handle simple dictionaries in a similar way.
class MyJSONEncoder(json.JSONEncoder): def __init__(self, *args, **kwargs): super(MyJSONEncoder, self).__init__(*args, **kwargs) self.current_indent = 0 self.current_indent_str = "" def encode(self, o): #Special Processing for lists if isinstance(o, (list, tuple)): primitives_only = True for item in o: if isinstance(item, (list, tuple, dict)): primitives_only = False break output = [] if primitives_only: for item in o: output.append(json.dumps(item)) return "[ " + ", ".join(output) + " ]" else: self.current_indent += self.indent self.current_indent_str = "".join( [ " " for x in range(self.current_indent) ]) for item in o: output.append(self.current_indent_str + self.encode(item)) self.current_indent -= self.indent self.current_indent_str = "".join( [ " " for x in range(self.current_indent) ]) return "[\n" + ",\n".join(output) + "\n" + self.current_indent_str + "]" elif isinstance(o, dict): output = [] self.current_indent += self.indent self.current_indent_str = "".join( [ " " for x in range(self.current_indent) ]) for key, value in o.items(): output.append(self.current_indent_str + json.dumps(key) + ": " + self.encode(value)) self.current_indent -= self.indent self.current_indent_str = "".join( [ " " for x in range(self.current_indent) ]) return "{\n" + ",\n".join(output) + "\n" + self.current_indent_str + "}" else: return json.dumps(o)
NOTE: It's pretty much unnecessary in this code to be inheriting from JSONEncoder
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With