Есть ли способ читать последовательно красиво напечатанные объекты JSON в Python?

Предположим, у вас есть такой файл JSON:

{
  "a": 0
}
{
  "a": 1
}

Это не JSONL, поскольку каждый объект занимает более одной строки. Но это также не единственный действительный объект JSON. Это последовательно перечисленные красиво напечатанные объекты JSON.

json.loads в Python выдает ошибку о недопустимом форматировании, если вы пытаетесь загрузить это, и в документации указано, что он загружает только один объект. Но такие инструменты, как jq, могут без проблем читать такого рода данные.

Есть ли какой-нибудь разумный способ работать с данными, отформатированными таким образом, с использованием основной библиотеки json? У меня проблема: у меня есть несколько сложных объектов, и хотя я просто форматирую данные, как работает JSONL, для удобства чтения было бы лучше хранить данные таким образом. Я могу обернуть все в список, чтобы сделать его одним объектом JSON, но у этого есть недостатки, например, необходимость чтения всего файла сразу.

Похожий вопрос здесь, но, несмотря на заголовок, данных вообще нет в формате JSON.

Объект может занимать более 1 строки. Вы можете прочитать фрагмент и проанализировать элементы между {} . Если это не json, то попытка использовать код, специально написанный для работы с json, обычно приводит к неудаче.

ticktalk 19.07.2024 07:27
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
83
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете частично декодировать текст в формате 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(), затем

  1. Если удалось, мы находимся в конце строки
  2. Если нет, загрузите объект до места ошибки, 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 доступен в объекте ошибки — я думаю, что возможность просто использовать ошибку в некотором смысле чище.

polm23 20.07.2024 08:19

Лучшим вариантом будет использование встроенной библиотеки Python pprint Здесь

материал — это объект словаря. Если json указан в файле, вы можете загрузить его, используя материал = json.load(путь_файла)

в противном случае, если это файл, вы можете использовать

материал = json.load(путь_файла). Что касается печати, ppprint сделает всю работу за вас.

pprint.pp(stuff)

Это не отвечает на вопрос? Вопрос не в том, как красиво распечатать, а в том, как читать такие данные.

polm23 20.07.2024 08:20

Другие вопросы по теме