Python: форматирование сырого JSON Печать
Добавил(а) microsin   

Предположим, что у нас есть вот такой сырой текст JSON следующего вида, без пробелов, новых строк и отступов:

{"id": "0001","type": "donut","name":"Cake","ppu": 0.55,"batters":{"batter":[{"id":"1001",
"type":"Regular"},{"id":"1002","type":"Chocolate"},{"id":"1003","type":"Blueberry"},{"id":
"1004","type":"Devil's Food"}]},"topping":[{"id":"5001","type":"None"},{"id":"5002","type":
"Glazed"},{"id":"5005","type":"Sugar"},{"id":"5007","type":"Powdered Sugar"},{"id":"5006",
"type":"Chocolate with Sprinkles"},{"id":"5003","type":"Chocolate"},{"id":"5004","type":
"Maple"}],"available":true,"meaning":null}

Как этот текст превратить в что-то более осмысленное и понятное?

Ниже показано несколько способов преобразования сырого JSON в форматированный текст. Кроме того, эти утилиты помогут проверить корректность синтаксиса JSON.

[Использование стандартных утилит Python]

Простое декодирование, без сортировки:

$ python3 -m json.tool file.json

С сортировкой ключей:

$ python3 -m json.tool --sort-keys file.json

С цветами (требуется pygments):

$ python3 -c "import json, sys; from pygments import highlight, lexers, \
 formatters; data=json.load(sys.stdin); json_str=json.dumps(data, indent=2); \
 print(highlight(json_str, lexers.JsonLexer(), formatters.TerminalFormatter()))" \
 < file.json

Компактный вывод, если необходимо удалить лишнее форматирование:

$ python3 -c "import json, sys; print(json.dumps(json.load(sys.stdin), \
 separators=(',',':')))" < file.json

#!/usr/bin/env python3
import json
import sys
import argparse
from pathlib import Path

def print_json(data, indent=2, level=0, is_last=True, prefix=''):
"""Рекурсивная функция для красивого вывода JSON""" if isinstance(data, dict): print(f"{prefix}{{") items = list(data.items()) for i, (key, value) in enumerate(items): is_last_item = (i == len(items) - 1) new_prefix = prefix + (' ' * indent) print(f"{new_prefix}{json.dumps(key)}: ", end='') if isinstance(value, (dict, list)): print() print_json(value, indent, level + 1, is_last_item, new_prefix) else: print_json(value, indent, level + 1, is_last_item, new_prefix) if not is_last_item: print(',') else: print() print(f"{prefix}}}", end='') elif isinstance(data, list): print(f"{prefix}[") for i, item in enumerate(data): is_last_item = (i == len(data) - 1) new_prefix = prefix + (' ' * indent) print_json(item, indent, level + 1, is_last_item, new_prefix) if not is_last_item: print(',') else: print() print(f"{prefix}]", end='') else: # Для простых типов данных print(f"{json.dumps(data)}", end='')

def colorize_json(json_str):
"""Цветной вывод JSON (если поддерживается терминал)""" try: from pygments import highlight, lexers, formatters colored = highlight( json_str, lexers.JsonLexer(), formatters.TerminalFormatter() ) return colored except ImportError: return json_str

def main(): parser = argparse.ArgumentParser( description='Преобразование сырого JSON в структурированный вид' ) parser.add_argument('input', nargs='?', help='Файл с JSON или строка JSON') parser.add_argument('-i', '--indent', type=int, default=2, help='Размер отступа (по умолчанию: 2)') parser.add_argument('-c', '--color', action='store_true', help='Цветной вывод (требуется pygments)') parser.add_argument('-f', '--file', action='store_true', help='Указать, что input это файл') parser.add_argument('-s', '--sort-keys', action='store_true', help='Сортировать ключи объектов') parser.add_argument('--compact', action='store_true', help='Компактный вывод (без лишних пробелов)') args = parser.parse_args() # Определяем источник данных json_data = None if args.input: if args.file: # Чтение из файла file_path = Path(args.input) if not file_path.exists(): print(f"Ошибка: файл '{args.input}' не найден", file=sys.stderr) sys.exit(1) with open(file_path, 'r', encoding='utf-8') as f: try: json_data = json.load(f) except json.JSONDecodeError as e: print(f"Ошибка парсинга JSON: {e}", file=sys.stderr) sys.exit(1) else: # Парсинг строки try: json_data = json.loads(args.input) except json.JSONDecodeError: # Возможно, это путь к файлу file_path = Path(args.input) if file_path.exists(): with open(file_path, 'r', encoding='utf-8') as f: try: json_data = json.load(f) except json.JSONDecodeError as e: print(f"Ошибка парсинга JSON: {e}", file=sys.stderr) sys.exit(1) else: print("Ошибка: невалидный JSON и файл не найден", file=sys.stderr) sys.exit(1) else: # Чтение из stdin try: json_data = json.load(sys.stdin) except json.JSONDecodeError as e: print(f"Ошибка парсинга JSON из stdin: {e}", file=sys.stderr) sys.exit(1) # Обработка параметров if args.compact: result = json.dumps(json_data, separators=(',', ':')) elif args.sort_keys: result = json.dumps(json_data, indent=args.indent, sort_keys=True, ensure_ascii=False) else: # Используем кастомный вывод import io output = io.StringIO() original_stdout = sys.stdout sys.stdout = output print_json(json_data, indent=args.indent) print() # Добавляем новую строку в конце sys.stdout = original_stdout result = output.getvalue() # Вывод результата if args.color: try: print(colorize_json(result)) except: print(result) else: print(result)

if __name__ == '__main__': main()

Пример использования:

$ python3 ./raw-JSON-decode.py --help
usage: raw-JSON-decode.py [-h] [-i INDENT] [-c] [-f] [-s] [--compact] [input]
Преобразование сырого JSON в структурированный вид
positional arguments: input Файл с JSON или строка JSON
options: -h, --help show this help message and exit -i, --indent INDENT Размер отступа (по умолчанию: 2) -c, --color Цветной вывод (требуется pygments) -f, --file Указать, что input это файл -s, --sort-keys Сортировать ключи объектов --compact Компактный вывод (без лишних пробелов)
$ python3 ./raw-JSON-decode.py -c ~/tmp/json-raw.json { "id": "0001", "type": "donut", "name": "Cake", "ppu": 0.55, "batters": { "batter": [ { "id": "1001", "type": "Regular" }, { "id": "1002", "type": "Chocolate" }, { "id": "1003", "type": "Blueberry" }, { "id": "1004", "type": "Devil's Food" } ] }, "topping": [ { "id": "5001", "type": "None" }, { "id": "5002", "type": "Glazed" }, { "id": "5005", "type": "Sugar" }, { "id": "5007", "type": "Powdered Sugar" }, { "id": "5006", "type": "Chocolate with Sprinkles" }, { "id": "5003", "type": "Chocolate" }, { "id": "5004", "type": "Maple" } ], "available": true, "meaning": null }