LINUX.ORG.RU

Распарсить json по мере поступления данных

 ,


0

3

Хочется иметь стандартный интерфейс для выделения обьектов из потока вида:

data = socket.read(1024)
parser.feed(data)
objects = parser.get_objects()
for o in objects:
    process_object(o)

Есть ли такая возможность в стандартном json модуле питона? Я что-то сходу не нашёл.

TL;DR: нет в стандартной библиотеке, такой возможности нет, максимум, можно откусить от строки кусок, с помощью JSONDecoder.raw_decode, но докормить парсер в случае если в первый раз пришла только половина документа, возможности нет.

Сторонние реализации гугляться по запросу python json stream.

★★★★★

Последнее исправление: pon4ik (всего исправлений: 2)

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

hippi90 ★★★★★
()

В стандартном емнип нет, ищи потоковые парсеры.

PolarFox ★★★★★
()
Ответ на: комментарий от kachan

Ну вот смотри, допустим ты читаешь с сети. Для наглядности возьмём udp. Есть характеристика сети mtu - максимум байт в пакете (обычно порядка 1500 байт). Опять же для наглядности возьмём mtu = 8.

Клиент шлёт запрос { «type»: «hello»}

Тогда ты получишь такие пакеты(если не заморачиваться с порядком пакетов и потерями, это уже ответственность транспорта):

{ "type"
: "hello
"}

Согласно спецификации json - документ всегда является объектом. Однако часто эту часть расширяют до условия что документ это обьект или массив обьектов.

Соответственно мы всегда можем пропустить байты в потоке до тех пор, пока у нас не появится потенциальное начала нового обьекта или массива и парсить посимвольно. Пропуск это уже опционально, т.к. наличие нештатных символов в потоке во многих случаях есть исключительная ситуация.

Вот пример реализации на том же python - naya. Однако, обидно, что такую элементарную вещь не сделали частью стандартной библиоткеи.

pon4ik ★★★★★
() автор топика
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Согласно спецификации json - документ всегда является объектом

Ссылку?

это уже ответственность транспорта

Тогда и передача длины сообщений — ответственность транспорта. Твой случай это не юзкейс для итеративного жсона.

Deleted
()
Ответ на: комментарий от Deleted

Я использую это описание нотации. С одной стороны спецификация это сильно сказано, с другой стороны её вполне можно использовать для построения реализаций.

Ecma документы - не читал.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Да, это я видал уже.

Не хочется сишную либу тащить. В прочем и питонячью не очень хочется ради такой элементарщины. Почитаю по внимательней спеку передающей стороны.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Твой случай это не юзкейс для итеративного жсона.

А передача документов длиннее буффера сокета с использованием aio это юзекейс?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Невнимательно прочитал описание, там есть реализация на pure python.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Почитал 404 - да, точно json text is value. Но это никак не отменяет, что каждый элемент можно однозначно выделить в потоке при условии, что мы читаем поток сначала.

Т.е. текст:

123"asd"{"key": "value"}

Можно однозначно интерпретировать в потоке как 3 json text документа, при чтении с начала потока.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Походу я просто не совсем правильно понял вопрос, спасибо за разъяснение.

kachan ★★
()
Ответ на: комментарий от pon4ik

элементарщины

Я как-то сомневаюсь, что там совсем уж элементарщина.

Не хочется сишную либу тащить.

Ну да, зачем быстро работающий код :)

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Ну да, зачем быстро работающий код :)

Ну а зачем он, если 99% времени тратится на i/o ?:) В общем - зависит от задачи, но мне миллисекундных лэйтенси за глаза, будет узко можно усложнять, а преждевременные оптимизации есть зло.

Я как-то сомневаюсь, что там совсем уж элементарщина.

Вот реализация, но почему-то, автор сделал блокирующий read(1) вместо функции feed, которая волшебным образом позволяет отвязаться от интерфейса чтения совсем.

500 строчек кода, половина из которого фактически переписывает спеку притом не самым лаконичным образом.

pon4ik ★★★★★
() автор топика

raw_decode(s)
Decode a JSON document from s (a str beginning with a JSON document) and return a 2-tuple of the Python representation and the index in s where the document ended.
This can be used to decode a JSON document from a string that may have extraneous data at the end.

Мне кажется что питонщики такие же как и жаваскриптеры, им проще наговнякать костылей чем разобраться в собственной доке

Deleted
()
Ответ на: комментарий от pon4ik

нет конечно, я лор читаю как и ты

Deleted
()
Ответ на: комментарий от theNamelessOne

Да, я тоже об этом думаю, учитывая, что сам проджект по фану, можно и повелосипедить.

pon4ik ★★★★★
() автор топика

TL;DR: Просто возьми готовую rpc-либу для обмена сообщениями и отдельно пакуй каждую сущность. Быстро, просто, надёжно. Накрайняк вот этот подход: https://developers.google.com/protocol-buffers/docs/techniques#streaming

А потоки... По-моему, эти потоки и навороченный парсинг путь в никуда. Я видел несколько проектов которые с этим в лужу сели. Например, тот же джаббер (привет, «поточный» xml). Каждый клиент изголялся по-своему с самопальными парсерами чтобы это прочесть. А «тестировали» это пользователи.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Хз не видел проблем с sax ни разу. Быстро и удобно. Что именно в джаббер с этим не в курсе, но сам по себе sax вполне себе удобно, особенно для сетевых дел.

Вот хотелось обойтись без либ, однако если обмазываться либами, то брать уж сразу json-rpc реализацию.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Я, возможно, чего-то не догоняю, но sax не идёт ни в какое сравнение с полноценным парсером. Одно дело на выходе сразу получить DOM сразу валидацией, другое дело руками его собирать самому. Но, выбор за тобой :).

хотелось обойтись без либ

нет проблем, бей поток на куски ручками, а каждый кусок это полноценный json. Вот это — самое нормальное архитектурное решение, поддерживаемое питоном из коробки.

А городить свой «поточный» json это пустая трата ресурсов, имхо. Даже для just for fun. Причём, городить огород нужно и на сервере и на клиенте.

true_admin ★★★★★
()
Ответ на: комментарий от pon4ik

Ага, моя архитектура говно

да, твоя архитектура говно

больше 20 объектов отображать в интерфейсе в единицу времени бессмысленно, с точки зрения интерфейса пользователя

сделай два сервиса-источника данных:
1. отдаёт 20 первых элементов массива
2. асинхронно отдаёт все остальные для последующей прокрутки

как и любого сетевого ПО.

не примазывайся, ты не в сетевом ПО, ни в пользовательских интерфейсах не понимаешь

anonymous
()
Ответ на: комментарий от anonymous

в единицу времени

Есть больше 3ех попугаев за раз вредно для здоровья.

больше 20 объектов отображать в интерфейсе,
с точки зрения интерфейса пользователя

При чём тут интерфейс пользователя? Я хоть слово сказал о ui? Ещё скажи что пару миллионов сообщений в секунду не нужно нигде ...

А теперь момент номер два, пусть весь твой лепет про интерфейс правда, скажи мне как выкачать два но длинных(длиннее буффера сокета принимающей стороны или длиннее окна) элемента в случае, если речь идёт например о tcp?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от true_admin

DOM сразу валидацией

В случае sax ты тоже вполне себе автоматически обработаешь ошибки, просто в другом стиле, в момент возникновения ошибки вызовется твой коллбэк.

sax такой же полноценный парсер. По сути, dom делает всё тоже что и sax, просто каждое событие записывается в древовидную структуру данных в памяти, корень которой обзывается как то в духе XmlDocument.

В случае использования sax ты экономишь на работе с памятью, это особенно критично в многопоточных приложениях и приложениях с высокими нефункциональными требованиями. Например, при чтении с сети пары десятков тысяч сообщений в секунду dom однозначно становится узким местом, особенно если тебя интересует два-три сообщения из этой массы и схема спроектирована достаточно грамотно что бы при начале разбора понять, что текущее сообщение тебя не интересует.

Более того, если посмотреть на реализацию любого нормально сделанного парсера, там по сути и будет sax подобный интерфейс, и, часто, какая то дефолтная структура куда можно записать элементы дерева в виде уже конечного интерфейса пользователя. Такова природа разбора форматов данных, что вполне естественно сообщать о событиях, классический пример:

2 + 2
// dom вариант: прочитали входной поток до конца, 
// если не нашли ошибок - получили на выходе структуру вида:
   +
 2   2
// sax подход:
известили клиента о том, что пришло число 2
известили клиента о том, что пришёл оператор +
известили клиента о том, что пришло число 2

Суть в том, что если клиенту нужно просто посчитать, ему нафиг не нужно предыдущее дерево в памяти и второй проход по нему с целью вычисления, он может вычислить всё что ему интересно прямо в процессе обработки событий.

бей поток на куски ручками

Я не могу переписать клиентскую сторону, и там как раз корректно обрабатываются большие сообщения.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

В первых четырех байтах передаешь длину сообщения, потом читаешь в свой буфер полученное количество байтов.

Deleted
()
Ответ на: комментарий от Deleted

Это ты мне предлагаешь свой протокол. Протокол есть, клиентскую часть я менять не могу.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Скажи архитектору протокола, что он даун. Голый жсон без признаков границ, по UDP… обосраться.

Deleted
()

TL;DR.

Будь мужиком:

  1. Возми jsonsl. Он на C.
  2. Строй модель данных по вкусу через FFI. В комплекте идёт пример для glib.
  3. ???
  4. PROFIT!!

И твои волосы будут мягкими и шелковистыми.

В центр JSON-парсера засовывают обычный рекурсивно-спусковой парсер. «Докормить» его невозможно (без шаманств с корутинами). Если используется лексер, то и лексер должен разбирать ситуацию когда EOF наступает посередине токена.

Поэтому, нужен парсер, оформленный по классической схеме со стеком. Таблица переходов для JSON будет проще классического МП-автомата т.к. помещение/извлечение значения в стек осуществляется безусловно по считыванию специального символа ({, }, [, ] и т.п.).

В случае JSON получается не очень стройно, т.к. при получении символа " парсер переходит в упрощённый режим работы пока не будет считана вся строка. На этом уровне приходится разбираться с эскейпингом, но язык регулярный.

Такая же хрень в случае чисел.

Почему таких парсеров меньшинство? Ну, потому что они сливают в хипстеробенчмарках, т.к. намного сложнее. Обычно в таких парсерах отсутствует модель данных, а есть только коллбэки, что для любителей смузи вообще китайская грамота.

Macil ★★★★★
()
Ответ на: комментарий от kachan

А это вообще реально? Json же должен иметь как начало так и конец.

Теоретически (возможно, с ограничениями) — реально.

Парсить потоком, а по мере закрытия элементов — возвращать их в какой-нибудь callback.

KRoN73 ★★★★★
()
Ответ на: комментарий от Deleted

Про udp - ты слышишь звон, но о чём он совершенно втыкать не хочешь. Udp был выбран для простоты обьяснения сути кейса когда мог бы понадобиться потоковый разбор. А так вот тебе кейс посложнее - select + tcp.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Но, даже по udp - размер нафик не нужен. Только номер пакета, ну и механизм перезапроса потерянных пакетов.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Macil

В случае входа в строку имхо достаточно сообщить о начале строки, конце строки и об ошибке если встречен недопустимый символ.

Кормить каждый символ клиенту конечно можно, но такое поведения явно должно быть за флагом, плюс тут надо понять кормить ли каждый символ или каждый байт, что в случае utf-8 вполне себе жизненно.

А так да, грамматика простая, изначальный вопрос был именно про стандартную библиотеку, на предмет вдруг чего я просмотрел или мыслю недостаточно pythonic например :)

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Проблема в тебе и твоем нежелании объяснять всю проблему целиком. С TCP нет никаких проблем в приведенной мною схеме.

Если ты выбрал интеграцию с идиотами, страдай на здоровье.

Deleted
()
Ответ на: комментарий от pon4ik

когда написал свой парсер JSON, то парсинг многомегабайтного документа из файла (без системных кэшей) ускорился в 5-10 раз по сравнению с jsoncpp, и при этом потребление памяти было O(1). А так да - производительность кода не нужна. Даёшь бубльсорт в массы!

dzidzitop ★★
()
Ответ на: комментарий от slaykovsky

код тут:

https://github.com/dzidzitop/gravifon_lastfm_scrobbler_deadbeef_plugins/blob/...

Почти уверен, что без знания/дебага он выглядит совсем непонятно (вариант - как неснятая георгиевская лента на антенне «бэхи» оголтелого «русского патриота» в марте). Поэтому прокомментирую ключевые моменты:

1) не используется динамическая память в принципе. Весь парсинг идёт на стэке

2) используется не универсальный парсер JSON, а набор утилит для построения своего парсера. Что-то типа лексера

3) используется знание о том, что за данные будут парсится. Например, что объект «duration» имеет два поля - величина и единица измерения («ms» или «s»). Но при этом если что-то идёт не так, то идёт сообщение об ошибке. Т.е. какашку скормить не получится.

Такой код пишется долго и нудно. Но зато потом работает быстро и экономично.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 2)
Ответ на: комментарий от pon4ik

Я с этим не спорю, не всегда можно засунуть весь dom в память. Но у меня сомнения вызывает правильность проектирование систем в которых есть потребность в многогигабайтных xml. Если вам нравится sax — используйте. Но задумайтесь над тем что может вы задачу не с того конца решаете.

особенно если тебя интересует два-три сообщения из этой массы и схема спроектирована достаточно грамотно

Я бы не назвал это «достаточно грамотной системой» когда по сети прилетает куча ненужных данных.

Я не могу переписать клиентскую сторону

А это как всегда. Сначала одни на одном конце фигню делают, потом другие на другом конце изобретают велосипеды с квадратными колёсами. Повторюсь, я бы рекомендовал вам задуматься над изменением протокола. Как минимум, сэкономите время на написании и тестировании парсера. Не, ну если just for fun, то почему бы и нет. Но лично меня больше вставляет написание архитектурно-грамотных решений, а не «посмотрите какой элегантный костыль мы придумали».

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

В групповом программировании «на дядю» этот код возник бы только при острой необходимости и выглядел бы чуть иначе. А сам себя я не уволю.

dzidzitop ★★
()

учитывая, что спецификация json простая как лопата, нет проблемы написать свой парсер если подходящего не нашлось. собственно мне так и пришлось делать, когда оказалось, что ни один и существующих для питона не умеет дергать переключение потоков (у меня гринлетов), если работает слишком долго. правда можно было еще взять саксовый и в нем это реализовать, но очень уж они садят скорость изза вызова коллбеков. а так свой потом на cython перевел и компильнул - по скорости получилось всегото в 5 раз медленнее самого быстрого. зато теперь при высокой нагрузке и большом колве соединений в очереди парсер начинает перещелкивать себя на другие задачи.

genryRar ★★
()
Ответ на: комментарий от true_admin

многогигабайтных

Никто не говорит о многогигобайтных. Когда требуется лэйтенси порядка микросекунд (а может и ранее) на килобайтных документах дом вполне себе сливает по производительности sax в большинстве разумных юзекейсов. Dom как раз сделан не столько для обмена сообщениями, сколько для длительной работы с одним документом. Т.е. в браузере логичней использовать dom подход, а при обмене сообщениями, в общем случае, sax подход.

Я бы не назвал это «достаточно грамотной системой» когда по сети прилетает куча ненужных данных

Расскажи это например биржам или операторам iptv или dns провайдерам. Да и просто разработчикам ethernet :)

По поводу костылестроения - я не вижу в чём тут костыль. Протокол установки поточного соединения реализован на уровне транспорта, т.е. об этом вообще можно не задумываться на данном уровне абстракции. Json, как формат данных позволяет совершенно однозначно идентефицировать сообщения в потоке без каких либо дополнительных телодвижений.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от genryRar

Проблемы нет. Вопрос был исключительно про интерфейсы стандартной библиотеки.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от dzidzitop

Если ты утилизируешь процессор хотя бы на 80%, то почему бы и нет. А если нет, то это выходка уровня смотрите как я могу :)

Профита в реальной системе от неё будет 0, оптимизировать надо узкое место. Если это стационарная система под конкретное окружение, то возможно за потраченное тобой время на подобные оптимизации выгоднее было бы купить 10G сетевуху например.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Проблем и без твоей схемы нет.

А в твоей схеме есть проблема - ты предлагаешь ввести дополнительный протокол. Не стандартизированный. С избыточными деталями не требующимися реализации. Ради чего? Ради того что бы использовать игрушечную реализацию json парсера для наколенных поделий вместо нормальной реализации. Я вижу мьсе большой мастер архитектурных решений. Особенно когда надо покукарекать какие все дебилы и что их солюшн кал.

Вот тебе простой вопрос, ответ на который совершенно не очевиден из твоего описания - в каком порядке байт нужно записывать размер сообщения? Т.е. на это нужно будет написать документацию как минимум. Зафиксировать в api что сии 4 байта надо будет интерпретировать именно так. Ну и поправить все уже имеющиеся реализации, сломав обратную совместимость.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Утилизирую процессор на 100 процентов. А время, потраченное на подобные оптимизации

1) дало набор инструментов для повторного использования

2) усовершенствовало навыки написания эффективного кода с оглядкой на то, какой выхлоп у компилятора

3) just for fun

А сеть у меня FastEthernet, и в целом хватает.

В командном программировании большинство подобных приёмов не применяю.

А по твоей теме - код выше является потоковым парсером json. Бери любую SAX-подобную либу для парсинга json и не слушай «архитекторов», которые советуют менять протокол.

dzidzitop ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.