Я использую следующий код для анализа многострочных объектов JSON, разделенных запятой, из веб-строки, хранящейся в файле .json:
import json
def stream_read_json(fn):
start_pos = 0
with open(fn, 'r', encoding='utf-8') as f:
while True:
try:
obj = json.load(f)
yield obj
return
except json.JSONDecodeError as e:
f.seek(start_pos)
json_str = f.read(e.pos)
obj = json.loads(json_str, encoding = 'utf-8')
start_pos += e.pos
yield obj
Первый объект анализируется правильно; следующие нет.
При тестировании случайных значений f.seek(start_pos)
я обнаружил несоответствие индексу, найденному except json.JSONDecodeError as e:
. Почему этот индекс отличается от количества символов, отображаемых при выборе в IDE текста до символа, на котором в файле заканчивается объект JSON?
Как я могу гарантировать, что объекты будут проанализированы правильно?
Я пытался получить f.seek(start_pos)
для второго объекта JSON в командной строке отладки, но он сильно отличается от e.pos
, выдаваемого ошибкой.
Пример JSON здесь:
{
"user": {
"id": 1,
"profile": {
"name": "Alice",
"age": 30
}
},
"product": {
"sku": "A1234",
"details": {
"name": "Laptop",
"price": 999.99
}
}
},
{
"user": {
"id": 2,
"profile": {
"name": "Bob",
"age": 22
}
},
"product": {
"sku": "A123w",
"details": {
"name": "Laptop",
"price": 9.99
}
}
}
Пожалуйста, дополните свой вопрос небольшими примерами содержимого файла. Необычно пытаться использовать json
для загрузки из файла, когда вы ожидаете ошибки декодирования.
Я пытаюсь использовать подход к ошибкам, чтобы определить конец/начало каждого объекта. Была предоставлена ссылка на образец JSON. Если для этого есть более элегантное решение без дополнительного импорта, я рад узнать
json.load(f)
собирается прочитать все содержимое файла. Я не понимаю, почему вы используете здесь слово «потоковая передача». файл json по определению должен содержать ОДИН самый внешний объект.
Чтобы быть более ясным, я отредактировал; это веб-строка, содержащая два объекта json. Я бы хотел разобраться с каждым отдельно. json.load(f)
возвращает JSONDecodeError: Extra data: line 955 column 6 (char 33213)
с предоставленным файлом.
Вопросы должны включать все необходимое для воспроизведения проблемы в самом вопросе, а не во внешнем файле размером 55 КБ, срок действия которого истекает через 6 дней. Попробуйте найти минимальный репродуктор - он не должен занимать 55 Кб.
«попытался получить f.seek(start_pos) для второго объекта JSON в приглашении отладки» — что произошло потом?
Кроме того, поиск произвольных позиций в текстовом режиме является неопределенным поведением.
«это строка, очищенная от Интернета». Как проводилось соскабливание? Исправлением может быть создание данных по-другому вместо записи недопустимого файла JSON.
Это определенно не тот способ, как это следует делать, но я предложу обходной путь для вашей конкретной ситуации.
json.load(f) возвращает JSONDecodeError: Дополнительные данные: строка
Проблема в том, что ваш «json» на самом деле не является json, потому что в нем отсутствуют скобки []
для списка объектов и много повторяющихся ключей. Но в качестве обходного пути вы можете сделать следующее:
import json
with open("test.json", "r") as file:
str_data: str = file.read()
data: list[dict] = json.loads(f"[{str_data}]")
for item in data:
...
потрясающее решение! именно то решение, на которое я надеялся. მადლობა!
Да, это можно сделать, но я не рекомендую этого делать.
Ваши проблемы:
encoding='utf-8'
к json.loads()
Я протестировал этот код:
def stream_read_json(fn):
start_pos = 0
with open(fn, 'r', encoding='utf-8') as f:
while True:
try:
obj = json.load(f)
yield obj
return
except json.JSONDecodeError as e:
f.seek(start_pos)
json_str = f.read(e.pos)
obj = json.loads(json_str)
yield obj
f.read(1)
start_pos += e.pos + 1
Я также протестировал этот код, который использует только значения, восстановленные из f.tell()
в качестве параметра для f.seek()
, как рекомендовано в документации.
def stream_read_json(fn):
with open(fn, 'r', encoding='utf-8') as f:
while True:
try:
start_pos = f.tell()
obj = json.load(f)
yield obj
return
except json.JSONDecodeError as e:
f.seek(start_pos)
json_str = f.read(e.pos)
obj = json.loads(json_str)
yield obj
f.read(1)
поэтому кажется, что в индексе произошло несоответствие из-за разных кодировок, используемых для чтения данных. хорошо подмечено!
Приведите пример данных, иначе вам никто не сможет помочь.