Created
August 9, 2024 14:52
-
-
Save spezold/0dbf5454200af85b2f04cd9986ba4fdb to your computer and use it in GitHub Desktop.
Dump given JSON document to a string, flattening lists (recursively) but respecting the indent for other objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
def json_dumps_compact(data, indent: int | str, **kwargs) -> str: | |
""" | |
Dump given JSON document to a string, flattening lists (recursively) but respecting the indent for other objects. | |
:param data: JSON object to be dumped | |
:param indent: indent level for JSON object members (None is not supported) | |
:param kwargs: other arguments passed to :func:`json.dumps` ("separators" is not supported) | |
:return: resulting string | |
""" | |
if indent is None: | |
raise ValueError("Unsupported argument 'indent=None' given") | |
if kwargs.get("separators") is not None: | |
raise ValueError("Unsupported argument 'separators' given") | |
# The "not" cases are necessary because empty lists/dicts by default end up with both brackets on the same line | |
is_list_start = lambda line_: line_.endswith("[") | |
is_list_end = lambda line_: line_.endswith(("],", "]")) and not line_.endswith(("[],", "[]")) | |
is_dict_start = lambda line_: line_.endswith("{") | |
is_dict_end = lambda line_: line_.endswith(("},", "}")) and not line_.endswith(("{},", "{}")) | |
at_bound = lambda lo, up: is_list_start(lo) or is_dict_start(lo) or is_list_end(up) or is_dict_end(up) | |
lines = json.dumps(data, indent=indent, **kwargs).split("\n") | |
processed_lines = [] | |
current_line = [] | |
lists_to_close = 0 | |
for line in lines: | |
# If a list starts, lift all following lines onto the same line until it is closed; | |
# strip indents for all except first start of a list | |
if is_list_start(line): | |
current_line.append(line if not lists_to_close else line.strip()) | |
lists_to_close += 1 | |
elif is_list_end(line): | |
lists_to_close -= 1 | |
current_line.append(line.strip()) | |
if not lists_to_close: | |
# Final list is closed: assemble line; provide separators only between actual items | |
seps = ("" if at_bound(lo, up) else " " for lo, up in zip(current_line[:-1], current_line[1:])) | |
processed_lines.append("".join(i for t in zip(current_line, seps) for i in t) + current_line[-1]) | |
current_line = [] | |
else: | |
if lists_to_close: | |
current_line.append(line.strip()) # Inside list → append to current line | |
else: | |
processed_lines.append(line) # Append to processed lines | |
return "\n".join(processed_lines) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment