123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
| import json
 | |
| 
 | |
| from functools import reduce
 | |
| 
 | |
| INDENT = '  '
 | |
| 
 | |
| class CollapseList(list):
 | |
|     pass
 | |
| class CollapseDict(dict):
 | |
|     pass
 | |
| class AlignedDict(dict):
 | |
|     def __init__(self, src_dict, depth):
 | |
|         self.depth = depth - 1
 | |
|         super().__init__(src_dict)
 | |
| class SortedDict(dict):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| def is_scalar(value):
 | |
|     return not is_list(value) and not is_dict(value)
 | |
| 
 | |
| 
 | |
| def is_list(value):
 | |
|     return isinstance(value, list) or isinstance(value, tuple)
 | |
| 
 | |
| 
 | |
| def is_dict(value):
 | |
|     return isinstance(value, dict)
 | |
| 
 | |
| 
 | |
| def dump_scalar(obj, ensure_ascii=False):
 | |
|     return json.dumps(obj, ensure_ascii=ensure_ascii)
 | |
| 
 | |
| 
 | |
| def dump_list(obj, current_indent='', ensure_ascii=False):
 | |
|     entries = [dump_obj(value, current_indent + INDENT, ensure_ascii=ensure_ascii) for value in obj]
 | |
| 
 | |
|     if len(entries) == 0:
 | |
|         return '[]'
 | |
| 
 | |
|     if isinstance(obj, CollapseList):
 | |
|         values_format = '{value}'
 | |
|         output_format = '[{values}]'
 | |
|         join_format   = ', '
 | |
|     else:
 | |
|         values_format = '{indent}{value}'
 | |
|         output_format = '[\n{values}\n{indent}]'
 | |
|         join_format   = ',\n'
 | |
| 
 | |
|     output = output_format.format(
 | |
|         indent=current_indent,
 | |
|         values=join_format.join([values_format.format(
 | |
|             value=entry,
 | |
|             indent=current_indent + INDENT
 | |
|         ) for entry in entries])
 | |
|     )
 | |
| 
 | |
|     return output
 | |
| 
 | |
| 
 | |
| def get_keys(obj, depth):
 | |
|     if depth == 0:
 | |
|         yield from obj.keys()
 | |
|     else:
 | |
|         for value in obj.values():
 | |
|             yield from get_keys(value, depth - 1)
 | |
| 
 | |
| 
 | |
| def dump_dict(obj, current_indent='', sub_width=None, ensure_ascii=False):
 | |
|     entries = []
 | |
| 
 | |
|     key_width = None
 | |
|     if sub_width is not None:
 | |
|         sub_width = (sub_width[0]-1, sub_width[1])
 | |
|         if sub_width[0] == 0:
 | |
|             key_width = sub_width[1]
 | |
| 
 | |
|     if isinstance(obj, AlignedDict):
 | |
|         sub_keys = get_keys(obj, obj.depth)
 | |
|         sub_width = (obj.depth, reduce(lambda acc, entry: max(acc, len(entry)), sub_keys, 0))
 | |
| 
 | |
|     for key, value in obj.items():        
 | |
|         entries.append((dump_scalar(str(key), ensure_ascii), dump_obj(value, current_indent + INDENT, sub_width, ensure_ascii)))
 | |
| 
 | |
|     if key_width is None:
 | |
|         key_width = reduce(lambda acc, entry: max(acc, len(entry[0])), entries, 0)
 | |
| 
 | |
|     if len(entries) == 0:
 | |
|         return '{}'
 | |
| 
 | |
|     if isinstance(obj, SortedDict):
 | |
|         entries.sort(key=lambda item: item[0])
 | |
| 
 | |
|     if isinstance(obj, CollapseDict):
 | |
|         values_format = '{key} {value}'
 | |
|         output_format = '{{{values}}}'
 | |
|         join_format   = ', '
 | |
|     else:
 | |
|         values_format = '{indent}{key:{padding}}{value}'
 | |
|         output_format = '{{\n{values}\n{indent}}}'
 | |
|         join_format   = ',\n'
 | |
| 
 | |
|     output = output_format.format(
 | |
|         indent=current_indent,
 | |
|         values=join_format.join([values_format.format(
 | |
|             key='{key}:'.format(key=key), 
 | |
|             value=value,
 | |
|             indent=current_indent + INDENT,
 | |
|             padding=key_width + 2,
 | |
|         ) for (key, value) in entries])
 | |
|     )
 | |
| 
 | |
|     return output
 | |
| 
 | |
| 
 | |
| def dump_obj(obj, current_indent='', sub_width=None, ensure_ascii=False):
 | |
|     if is_list(obj):
 | |
|         return dump_list(obj, current_indent, ensure_ascii)
 | |
|     elif is_dict(obj):
 | |
|         return dump_dict(obj, current_indent, sub_width, ensure_ascii)
 | |
|     else:
 | |
|         return dump_scalar(obj, ensure_ascii)
 |