Предположим, что у нас есть вот такой сырой текст 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 Pathdef 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_strdef 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
}