Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

Пости з тегом #JSON

17.01.2025

Формат JSONLines

JSON всім чудовий формат даних - текстовий, легко читається та пишеться людьми, швидко читається та пишеться машинами завдяки багатьом оптимізаціям. Але є в нього одне проблемне місце: щоб щось зробити з документом JSON, його доведеться повністю завантажити в памʼять. Коли даних стає сотні мегабайт, то завантаження їх в памʼять стає серйозним випробуванням: бо внутрішнє представлення може зайняти ще більше місця.

Перед тим, як обирати з форматів для Big Data, краще подивитися на JSON Lines. Це формат масиву даних, де в кожному рядку міститься повний документ JSON. (Звісно, при цьому розриви рядка в середині документа заборонені.)

Перевага JSONLines: кожен рядок можна читати та обробляти окремо (та вивільняти памʼять). Також можна, навіть без розбору змісту JSON, орієнтуватися в масиві — достатньо стежити за закінченнями рядка, щоб визначити місце, полічити записи, розділити масив (чи склеїти два!) тощо.

Недолік JSONLines: він все ж не такий загально прийнятний, як JSON. Також людям його читати важче. Ну й те, що JSONLines - обовʼязково масив. Тому сфера застосування — скоріше, машинні документи, чи запити до API. Я бачив JSONLines в пакетних запитах ElasticSearch, експортах AWS Redshift, AWS Kinesis Firehose… вебхуках Mailtrap. :) Раджу мати на озброєнні.

Ще дотепний факт: насправді є цілих два стандарти для JSON Lines - другий називається NDJson та функціонально ідентичний, за винятком медіатипу.


18.01.2025

Потокова обробка JSON

Поки писав вчора про JSONLines, встиг дізнатися про альтернативу. Звичайний JSON теж можна обробляти потоком. Я маю на увазі, зчитувати частину, обробляти та вивільняти — звісно, коли йдеться про якийсь масив незалежних даних.

Адже ще одна перевага JSON - це його контекстно-незалежна рекурсивність (сам тільки що придумав). Вкладені обʼєкти синтаксично еквівалентні цілому документу. Нам не потрібно знати нічого про відступи, області імен, якісь посилання тощо. Якщо ми зчитуємо з потоку JSON та встановили вказівник в позицію початку вкладеного значення, то парсер буде готовий взяти та зчитати це значення, наче окрім нього нічого не існує. Залишиться тільки просувати вказівник до наступного значення, тобто повз кому.

Я все це не вигадав, а прочитав у прикладі до методу json.Decoder.Decode() з Go. Бо в Go декодер JSON відкриває достатньо інтерфейсу для поміркованого читання; можна читати цілий документ, а можна значення за значенням. В цілому, так можна зробити з будь-яким парсером, що читає потік — але ж пропускати коми та пробіли доведеться вручну.

Цікаво, що в JavaScript взагалі не бачу рідного модуля для читання JSON з потоку, а тільки з рядка. Знайшов ось stream-json, яка щось таке вміє. (А от JSONLines можна легко читати з потоку через модуль readline та обробляти рядок за рядком.)

Як бачимо, з одного боку, витончений читач може реалізувати потокову обробку й без обтяження споживача незвичним форматом даних. Але з іншого боку, JSONLines гарний тим, що там один документ на рядок, та працювати з ним можна звичайними текстовими засобами (наприклад, wc -l, щоб підрахувати кількість документів.)


19.01.2025

Потоковий запис JSON

Я в попередньому пості писав про читання JSON поступово з вивільненням памʼяті, але ж хтось той JSON повинен був записати? Ситуація з записом значно простіша, та не потребує ніяких особливих інструментів.

Почнемо з того, що генерація JSON можлива в один прохід та може здійснюватись у вихідний потік. Можна навіть поєднати її зі стисканням в gzip та пересилати через мережу з мінімальним буфером для виходу.

(До речі: gzip стискає зміст через заміну повторюваних послідовностей. Таким чином, назви атрибутів та інші повторювані рядки в JSON - ідеальні вхідні дані для gzip.)

Але — інколи в нас немає всіх даних відразу — наприклад, вони не влазять в памʼять, або ми отримуємо їх пакетами з Kafka чи ще звідкілясь. Навіть в такому випадку нескладно утворити на виході єдиний JSON без залучення великого буфера.

Наприклад. Пишемо у вихід [ - початок масиву. Далі пишемо по черзі стільки даних, скільки потрібно — читаючи з бази або з генератора або де там ще в нас дані беруться. Наприкінці закриваємо масив: ]. От і все!

Інколи все, що потрібно — це трохи текстових операцій. Я колись так робив, щоб зливати з Kafka зміст для резервного копіювання.