Несогласованность индексов символов при попытке анализа нескольких JSON в файле

Я использую следующий код для анализа многострочных объектов 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
    }
  }
}

Приведите пример данных, иначе вам никто не сможет помочь.

Jeremy Fiel 25.07.2024 22:14

Пожалуйста, дополните свой вопрос небольшими примерами содержимого файла. Необычно пытаться использовать json для загрузки из файла, когда вы ожидаете ошибки декодирования.

quamrana 25.07.2024 22:14

Я пытаюсь использовать подход к ошибкам, чтобы определить конец/начало каждого объекта. Была предоставлена ​​ссылка на образец JSON. Если для этого есть более элегантное решение без дополнительного импорта, я рад узнать

Martin Horst 25.07.2024 22:32
json.load(f) собирается прочитать все содержимое файла. Я не понимаю, почему вы используете здесь слово «потоковая передача». файл json по определению должен содержать ОДИН самый внешний объект.
John Gordon 25.07.2024 22:45

Чтобы быть более ясным, я отредактировал; это веб-строка, содержащая два объекта json. Я бы хотел разобраться с каждым отдельно. json.load(f) возвращает JSONDecodeError: Extra data: line 955 column 6 (char 33213) с предоставленным файлом.

Martin Horst 25.07.2024 22:50

Вопросы должны включать все необходимое для воспроизведения проблемы в самом вопросе, а не во внешнем файле размером 55 КБ, срок действия которого истекает через 6 дней. Попробуйте найти минимальный репродуктор - он не должен занимать 55 Кб.

user2357112 25.07.2024 22:53

«попытался получить f.seek(start_pos) для второго объекта JSON в приглашении отладки» — что произошло потом?

mkrieger1 25.07.2024 22:54

Кроме того, поиск произвольных позиций в текстовом режиме является неопределенным поведением.

user2357112 25.07.2024 22:54

«это строка, очищенная от Интернета». Как проводилось соскабливание? Исправлением может быть создание данных по-другому вместо записи недопустимого файла JSON.

Mark Tolonen 25.07.2024 23:22
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
9
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Это определенно не тот способ, как это следует делать, но я предложу обходной путь для вашей конкретной ситуации.

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:
    ...

потрясающее решение! именно то решение, на которое я надеялся. მადლობა!

Martin Horst 26.07.2024 14:03

Да, это можно сделать, но я не рекомендую этого делать.

Ваши проблемы:

  1. Вы добавили encoding='utf-8' к json.loads()
  2. Вы игнорируете запятую, разделяющую ваши объекты 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)
                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)

поэтому кажется, что в индексе произошло несоответствие из-за разных кодировок, используемых для чтения данных. хорошо подмечено!

Martin Horst 26.07.2024 14:07

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