Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON dumps custom formatting

Tags:

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).

like image 573
Saar Drimer Avatar asked Apr 28 '13 15:04

Saar Drimer


People also ask

What's the difference between JSON dump and JSON dumps?

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.

What is JSON dumps () method?

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.

Does JSON dumps convert to string?

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.

What is indent in JSON dumps?

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.


2 Answers

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.

like image 200
jmm Avatar answered Oct 28 '22 03:10

jmm


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.

like image 44
Tim Ludwinski Avatar answered Oct 28 '22 04:10

Tim Ludwinski