Я хотел бы читать несколько объектов JSON из файла / потока в Python по одному. К сожалению, json.load()
только .read()
до конца файла; похоже, нет никакого способа использовать его для чтения одного объекта или для ленивого перебора объектов.
Есть какой-либо способ сделать это? Идеально было бы использовать стандартную библиотеку, но если есть сторонняя библиотека, я бы использовал ее.
На данный момент я помещаю каждый объект в отдельную строку и использую json.loads(f.readline())
, но я бы предпочел не делать этого.
Пример использования
example.py
import my_json as json
import sys
for o in json.iterload(sys.stdin):
print("Working on a", type(o))
in.txt
{"foo": ["bar", "baz"]} 1 2 [] 4 5 6
пример сеанса
$ python3.2 example.py < in.txt
Working on a dict
Working on a int
Working on a int
Working on a list
Working on a int
Working on a int
Working on a int
python
json
serialization
Джереми
источник
источник
{"foo": ["bar", "baz"]}
в моем примере), он долженyield
это сделать, а затем перейти к следующему (1
).'\n'
(одной новой строки, а не двух символов) в его json-представлении, потому что он'\n'
должен быть экранирован внутри строки json и, следовательно,'\n'
может использоваться только для форматирования, например, я считаю, чтоjson.dumps()
не t вводить'\n'
по умолчанию. Помните, что символы новой строки Unicode, такие как U + 0085, могут быть неэкранированы внутри строк json.Ответы:
Вот гораздо более простое решение. Секрет в том, чтобы попытаться, потерпеть неудачу и использовать информацию в исключении для правильного анализа. Единственное ограничение - файл должен быть доступен для поиска.
Изменить: только что заметил, что это будет работать только для Python> = 3.5. Ранее при сбоях возвращалась ошибка ValueError, и вам нужно было проанализировать позицию из строки, например
источник
re
не сработает - нужно убрать обратную косую черту. Рассмотрим необработанную строкуr'...'
.ujson
вместо этого,json
вы получите огромное ускорениеJSON обычно не очень подходит для такого рода инкрементального использования; не существует стандартного способа сериализации нескольких объектов, чтобы их можно было легко загружать по одному, без синтаксического анализа всей партии.
Решение «объект на строку», которое вы используете, можно увидеть и в другом месте. Scrapy называет это «строками JSON»:
Вы можете сделать это немного более Python:
Я думаю, что это лучший способ - он не полагается на какие-либо сторонние библиотеки, и легко понять, что происходит. Я также использовал его в своем собственном коде.
источник
Возможно, немного поздно, но у меня была именно эта проблема (ну, более или менее). Мое стандартное решение этих проблем обычно состоит в том, чтобы просто разделить регулярное выражение на некоторый хорошо известный корневой объект, но в моем случае это было невозможно. Единственный возможный способ сделать это в общем случае - реализовать правильный токенизатор .
Не найдя достаточно общего и достаточно хорошо работающего решения, я закончил делать это сам, написав
splitstream
модуль. Это пре-токенизатор, который понимает JSON и XML и разбивает непрерывный поток на несколько частей для синтаксического анализа (однако фактический синтаксический анализ остается на ваше усмотрение). Чтобы добиться от него некоторой производительности, он написан как модуль C.Пример:
источник
Конечно, ты справишься. Вы просто должны принять это
raw_decode
напрямую. Эта реализация загружает весь файл в память и работает с этой строкой (как иjson.load
делает); если у вас есть большие файлы, вы можете изменить его, чтобы читать из файла только по мере необходимости.Использование: как вы и просили, это генератор.
источник
На самом деле это довольно неприятная проблема, потому что вам нужно выполнять потоковую передачу по строкам, но сопоставление шаблонов в нескольких строках с фигурными скобками, а также сопоставление с шаблоном json. Это своего рода предварительная обработка json, за которой следует синтаксический анализ json. Json, по сравнению с другими форматами, легко анализировать, поэтому не всегда нужно использовать библиотеку синтаксического анализа, тем не менее, как мы должны решить эти конфликтующие проблемы?
Генераторы спешат на помощь!
Прелесть генераторов для подобных задач в том, что вы можете складывать их друг на друга, постепенно абстрагируя сложность задачи, сохраняя при этом лень. Я также рассматривал возможность использования механизма для передачи значений обратно в генератор (send ()), но, к счастью, обнаружил, что мне это не нужно.
Для решения первой проблемы вам понадобится какой-то streamingfinditer, как потоковая версия re.finditer. Моя попытка сделать это ниже втягивает строки по мере необходимости (раскомментируйте оператор отладки, чтобы увидеть), все еще возвращая совпадения. Затем я немного изменил его, чтобы получить несовпадающие строки, а также совпадения (отмеченные как 0 или 1 в первой части полученного кортежа).
При этом затем можно сопоставлять фигурные скобки, каждый раз учитывать, сбалансированы ли фигурные скобки, а затем возвращать простые или составные объекты в зависимости от ситуации.
Это возвращает кортежи следующим образом:
В основном это самая неприятная часть. Теперь нам просто нужно выполнить последний уровень синтаксического анализа, который мы сочтем нужным. Например, мы можем использовать функцию iterload Джереми Романа (спасибо!) Для синтаксического анализа одной строки:
Попробуй это:
Я получаю следующие результаты (и если вы включите эту строку отладки, вы увидите, что она вытягивает строки по мере необходимости):
Это не сработает для всех ситуаций. Из-за реализации
json
библиотеки невозможно полностью корректно работать без переопределения парсера самостоятельно.источник
"}"
и"]"
? Я думаю, что это общее ограничение синтаксического анализа с регулярным выражением.Я считаю, что лучший способ сделать это - использовать конечный автомат. Ниже приведен пример кода, который я разработал, преобразовав код NodeJS по приведенной ниже ссылке на Python
3 (используемое нелокальное ключевое слово доступно только в Python 3, код не будет работать на Python 2)Edit-1: обновлен и сделан код совместимым с Python 2
Edit-2: обновлена и добавлена версия только для Python3.
https://gist.github.com/creationix/5992451
Только версия Python 3
Версия, совместимая с Python 2
Тестируем это
Результат того же
источник
Я хочу предложить решение. Ключевая мысль - «попытаться» декодировать: если это не удается, дайте ему больше потока, в противном случае используйте информацию смещения для подготовки следующего декодирования.
Однако текущий модуль json не может допускать декодирования SPACE в заголовке строки, поэтому я должен их удалить.
========================= Я протестировал несколько файлов txt, и он отлично работает. (in1.txt)
(in2.txt)
(in.txt, ваш инициал)
(вывод для теста Бенедикта)
источник
Вот мой:
источник
Я использовал элегантное решение @wuilang. Простой подход - прочитать байт, попытаться декодировать, прочитать байт, попытаться декодировать, ... - работал, но, к сожалению, очень медленно.
В моем случае я пытался прочитать из файла «красиво напечатанные» объекты JSON того же типа. Это позволило мне оптимизировать подход; Я мог читать файл построчно, декодируя только тогда, когда нашел строку, содержащую точно "}":
Если вам довелось работать с однострочным компактным JSON, который экранирует символы новой строки в строковых литералах, вы можете смело упростить этот подход еще больше:
Очевидно, что эти простые подходы работают только для очень определенных типов JSON. Однако, если эти предположения верны, эти решения работают правильно и быстро.
источник
Если вы используете экземпляр json.JSONDecoder, вы можете использовать
raw_decode
функцию-член. Он возвращает кортеж Python-представления значения JSON и индекс остановки синтаксического анализа. Это позволяет легко нарезать (или искать в объекте потока) оставшиеся значения JSON. Я не очень доволен дополнительным циклом while, позволяющим пропускать пробелы между различными значениями JSON во входных данных, но, на мой взгляд, он выполняет свою работу.Следующая версия намного короче и съедает часть строки, которая уже проанализирована. Похоже, что по какой-то причине второй вызов json.JSONDecoder.raw_decode (), кажется, не работает, когда первый символ в строке является пробелом, это также причина, по которой я пропускаю пробелы в цикле while выше ...
В документации по классу json.JSONDecoder метод raw_decode https://docs.python.org/3/library/json.html#encoders-and-decoders содержит следующее:
И эти посторонние данные легко могут быть другим значением JSON. Другими словами, метод может быть написан с этой целью.
Используя input.txt, используя верхнюю функцию, я получаю пример вывода, представленный в исходном вопросе.
источник
Вы можете использовать https://pypi.org/project/json-stream-parser/ именно для этой цели.
вывод
источник