Reputation: 1191
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).
Upvotes: 25
Views: 21273
Reputation: 394
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.
Upvotes: 17
Reputation: 2863
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
.
Upvotes: 9
Reputation:
You will need to create a subclass of the json.JSONEncoder class and override the methods for each type of value so they write the format you need. You may end up re-implementing most of them, depending on what your formatting needs are.
http://docs.python.org/2/library/json.html has an example for extending the JSONEncoder.
Upvotes: 3