Предположим, у вас есть такой файл JSON:
{
"a": 0
}
{
"a": 1
}
Это не JSONL, поскольку каждый объект занимает более одной строки. Но это также не единственный действительный объект JSON. Это последовательно перечисленные красиво напечатанные объекты JSON.
json.loads
в Python выдает ошибку о недопустимом форматировании, если вы пытаетесь загрузить это, и в документации указано, что он загружает только один объект. Но такие инструменты, как jq
, могут без проблем читать такого рода данные.
Есть ли какой-нибудь разумный способ работать с данными, отформатированными таким образом, с использованием основной библиотеки json? У меня проблема: у меня есть несколько сложных объектов, и хотя я просто форматирую данные, как работает JSONL, для удобства чтения было бы лучше хранить данные таким образом. Я могу обернуть все в список, чтобы сделать его одним объектом JSON, но у этого есть недостатки, например, необходимость чтения всего файла сразу.
Похожий вопрос здесь, но, несмотря на заголовок, данных вообще нет в формате JSON.
Вы можете частично декодировать текст в формате JSON с помощью json.JSONDecoder.raw_decode. Этот метод возвращает кортеж из двух частей разобранного объекта и конечный индекс объекта в строке, который затем можно использовать в качестве начального индекса для частичного декодирования текста для следующего объекта JSON:
import json
def iter_jsons(jsons, decoder=json.JSONDecoder()):
index = 0
while (index := jsons.find('{', index)) != -1:
data, index = decoder.raw_decode(jsons, index)
yield data
так что:
jsons = '''\
{
"a": 0
}
{
"a": 1
}'''
for j in iter_jsons(jsons):
print(j)
выходы:
{'a': 0}
{'a': 1}
Демо здесь
Обратите внимание, что начальный индекс в качестве второго аргумента json.JSONDecoder.raw_decode
является деталью реализации, и что если вы хотите придерживаться общедоступного API, вам придется использовать менее эффективный подход разрезания строки (который включает копирование строки ) из индекса, прежде чем передать его в raw_decode
:
def iter_jsons(jsons, decoder=json.JSONDecoder()):
index = 0
while (index := jsons.find('{', index)) != -1:
data, index = decoder.raw_decode(jsons := jsons[index:])
yield data
Вот способ: попытайтесь json.loads()
, затем
error.pos
Код:
import json
text = """
{
"a": 0
}
{
"a": 1
}
"""
obj_list = []
while True:
try:
obj_list.append(json.loads(text))
# Success means we have reached the end of the string
break
except json.decoder.JSONDecodeError as error:
# error.pos is where the error happens within the text
valid_text, text = text[:error.pos], text[error.pos:]
obj_list.append(json.loads(valid_text))
print(obj_list)
Мы должны преобразовать текст в действительный текст JSON перед декодированием:
import json
import re
text = """
{
"a": 0
}
{
"a": 1
}
"""
text = "[" + re.sub(r'}\s+{', '},{', text) + "]"
obj_list = json.loads(text)
print(obj_list)
Спасибо за публикацию этого ответа! Я принял другой, но не осознавал, что pos
доступен в объекте ошибки — я думаю, что возможность просто использовать ошибку в некотором смысле чище.
Лучшим вариантом будет использование встроенной библиотеки Python pprint Здесь
материал — это объект словаря. Если json указан в файле, вы можете загрузить его, используя материал = json.load(путь_файла)
в противном случае, если это файл, вы можете использовать
материал = json.load(путь_файла). Что касается печати, ppprint сделает всю работу за вас.
pprint.pp(stuff)
Это не отвечает на вопрос? Вопрос не в том, как красиво распечатать, а в том, как читать такие данные.
Объект может занимать более 1 строки. Вы можете прочитать фрагмент и проанализировать элементы между {} . Если это не json, то попытка использовать код, специально написанный для работы с json, обычно приводит к неудаче.