Формат JSON Lines: работа с потоковыми JSON данными в Python
Что такое JSON Lines и зачем он нужен
JSON Lines (расширение .jsonl или .ndjson) - это текстовый формат, в котором каждая строка файла содержит один самостоятельный JSON объект. В отличие от обычного JSON массива, такой формат позволяет обрабатывать данные построчно, не загружая весь файл в память. Это особенно удобно для больших наборов данных, логов, потоковой передачи и интеграции с системами, поддерживающими построчную обработку (например, Hadoop, Spark).
Какое решение является самым эффективным для работы с JSON Lines в чистом Python?
Наиболее эффективный подход - использование встроенного модуля json в сочетании с построчным чтением файла через генератор. Этот метод не требует установки дополнительных библиотек, позволяет обрабатывать файлы любого размера и даёт полный контроль над ошибками.
import json
def read_jsonl(filepath):
"""Генератор, построчно читающий JSON Lines файл."""
with open(filepath, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue # пропускаем пустые строки
try:
yield json.loads(line)
except json.JSONDecodeError as e:
# логируем ошибку и продолжаем
print(f"Пропущена строка: {e}")
# Пример записи
def write_jsonl(filepath, records):
with open(filepath, 'w', encoding='utf-8') as f:
for record in records:
f.write(json.dumps(record, ensure_ascii=False) + '\n')
# Использование
for obj in read_jsonl('data.jsonl'):
print(obj.get('name'))Python 3 json (работа с json в python 3)
Типичные проблемы и их решения
- Пустые строки - вызывают
json.JSONDecodeError. Решение: проверятьif not line: continue. - Неверная кодировка - файл может быть в другой кодировке (например, latin-1). Решение: указать правильную кодировку при открытии:
encoding='latin-1'. - Символы перевода строки внутри JSON - если значение содержит новую строку, это нарушит формат. Решение: убедиться, что записываемые объекты сериализуются в одну строку (стандартный
json.dumpsне вставляет переносов). - Файл с BOM (Byte Order Mark) - добавление символа BOM в начале. Решение:
encoding='utf-8-sig'.
Как использовать библиотеку jsonlines для удобной работы с JSON Lines?
Библиотека jsonlines предоставляет высокоуровневый API для чтения, записи и обработки JSON Lines с автоматическим управлением ошибками и поддержкой контекстных менеджеров.
import jsonlines
# Чтение
with jsonlines.open('data.jsonl') as reader:
for obj in reader:
print(obj)
# Запись
records = [{'a': 1}, {'b': 2}]
with jsonlines.open('output.jsonl', mode='w') as writer:
writer.write_all(records)
# Фильтрация на лету
with jsonlines.open('data.jsonl') as reader:
filtered = (obj for obj in reader if obj.get('active'))
with jsonlines.open('active.jsonl', mode='w') as writer:
writer.write_all(filtered)Json open python (открытие json файла в python)
Проблемы: библиотека требует установки (pip install jsonlines), но решает многие рутинные задачи, такие как пропуск пустых строк и корректная обработка ошибок.
Как обработать огромный файл JSON Lines потоково с помощью ijson?
Для файлов, где каждая строка сама по себе велика или содержит вложенные структуры, можно использовать потоковый парсер ijson, который не загружает целиком ни строку, ни объект.
import ijson
import json
def iter_jsonl_stream(filepath):
with open(filepath, 'rb') as f:
# ijson работает с байтовыми потоками
for obj in ijson.items(f, 'item'):
yield obj
# Пример использования
for record in iter_jsonl_stream('bigdata.jsonl'):
process(record)
преобразовать json в словарь python (преобразование json в словарь python)
ijson требует, чтобы корневой элемент был задан (здесь 'item'), и удобен только для объектов не содержащих массивов на верхнем уровне. Для JSON Lines, где каждая строка отдельный объект, можно просто читать строки и парсить стандартным методом.
Как загрузить JSON Lines в pandas DataFrame?
Функция pd.read_json с параметром lines=True позволяет напрямую прочитать JSON Lines файл в DataFrame.
import pandas as pd
df = pd.read_json('data.jsonl', lines=True)
print(df.head())
# Запись DataFrame обратно в JSON Lines
df.to_json('output.jsonl', orient='records', lines=True, force_ascii=False)сохранить json python (сохранение json в файл python)
Проблемы:
- При больших файлах pandas загружает весь файл в память, что может быть неэффективно.
- Если строки имеют разную структуру, pandas заполняет пропуски NaN.
Как выполнить фильтрацию и трансформацию записей на лету?
Можно комбинировать генераторы с функциями преобразования.
def transform(record):
record['processed'] = True
return record
def filter_func(record):
return record.get('score', 0) > 0.5
with open('data.jsonl') as f:
records = (json.loads(line) for line in f if line.strip())
filtered = filter(filter_func, records)
transformed = map(transform, filtered)
for r in transformed:
print(r)Python json lib (библиотека json в python)
Как работать с сжатыми JSON Lines файлами (gzip, bz2)?
Использование модуля gzip или bz2 для открытия файла.
import gzip
import json
def read_gzip_jsonl(filepath):
with gzip.open(filepath, 'rt', encoding='utf-8') as f:
for line in f:
yield json.loads(line.strip())
# Запись сжатого файла
def write_gzip_jsonl(filepath, records):
with gzip.open(filepath, 'wt', encoding='utf-8') as f:
for rec in records:
f.write(json.dumps(rec, ensure_ascii=False) + '\n')При работе с большими архивами важно помнить, что gzip последователен. Для параллельной обработки потребуется распаковка или использование специализированных форматов.
Расширенные примеры работы с JSON Lines
Ниже приведены примеры для нестандартных сценариев использования.
1. Объединение нескольких JSON Lines файлов в один
import json
def merge_jsonl_files(input_files, output_file):
with open(output_file, 'w', encoding='utf-8') as out:
for fname in input_files:
with open(fname, 'r', encoding='utf-8') as inf:
for line in inf:
if line.strip():
out.write(line)
merge_jsonl_files(['part1.jsonl', 'part2.jsonl'], 'merged.jsonl')
print("Файлы объединены")(результат: создан merged.jsonl, содержащий все строки)
2. Параллельная обработка с multiprocessing
import json
import multiprocessing as mp
def process_line(line):
obj = json.loads(line.strip())
obj['triple'] = obj['value'] * 3
return json.dumps(obj, ensure_ascii=False) + '\n'
def parallel_process_jsonl(input_file, output_file, num_workers=4):
with open(input_file, 'r') as f:
lines = f.readlines()
with mp.Pool(num_workers) as pool:
processed = pool.map(process_line, lines)
with open(output_file, 'w') as f:
f.writelines(processed)
parallel_process_jsonl('data.jsonl', 'processed.jsonl')
print("Параллельная обработка завершена")3. Асинхронное чтение с asyncio
import asyncio
import json
def read_line_gen(filepath):
with open(filepath, 'r') as f:
for line in f:
if line.strip():
yield line
async def process_async(filepath):
loop = asyncio.get_event_loop()
with open(filepath, 'r') as f:
async for line in async_iter_lines(f):
obj = json.loads(line.strip())
# асинхронная обработка
print(obj)
# Вспомогательная функция
def async_iter_lines(file_obj):
async def wrapper():
for line in file_obj:
yield line
return wrapper()
asyncio.run(process_async('data.jsonl'))(Асинхронный вывод может быть перемешан, но каждый объект обработан)
4. Валидация схемы записей при чтении
import json
from jsonschema import validate, ValidationError
SCHEMA = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"}
},
"required": ["id", "name"]
}
def read_validated(filepath):
with open(filepath, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
obj = json.loads(line)
try:
validate(instance=obj, schema=SCHEMA)
yield obj
except ValidationError as e:
print(f"Невалидная запись: {e}")
for rec in read_validated('data.jsonl'):
print(rec['name'])5. Конвертация CSV в JSON Lines
import csv
import json
def csv_to_jsonl(csv_file, jsonl_file, delimiter=','):
with open(csv_file, 'r', newline='', encoding='utf-8') as csvf:
reader = csv.DictReader(csvf, delimiter=delimiter)
with open(jsonl_file, 'w', encoding='utf-8') as jsonlf:
for row in reader:
jsonlf.write(json.dumps(row, ensure_ascii=False) + '\n')
csv_to_jsonl('input.csv', 'output.jsonl')
print("Конвертация завершена")(создан файл output.jsonl, каждая строка - JSON объект с ключами из заголовков CSV)
6. Использование памяти через mmap для больших файлов
import mmap
import json
def fast_read_jsonl(filepath):
with open(filepath, 'r+b') as f:
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
pos = 0
while True:
end = mm.find(b'\n', pos)
if end == -1:
line = mm[pos:]
if line:
yield json.loads(line.decode('utf-8').strip())
break
line = mm[pos:end]
if line:
yield json.loads(line.decode('utf-8').strip())
pos = end + 1
mm.close()
for obj in fast_read_jsonl('large.jsonl'):
# быстрый потоковый доступ
pass(метод полезен для очень больших файлов, снижает нагрузку на копирование строк в память)
7. Построчная запись с атомарностью через временный файл
import json
import tempfile
import os
def safe_write_jsonl(filename, records):
tmp = filename + '.tmp'
with open(tmp, 'w', encoding='utf-8') as f:
for rec in records:
f.write(json.dumps(rec, ensure_ascii=False) + '\n')
os.replace(tmp, filename)
safe_write_jsonl('important.jsonl', [{'x': 1}, {'y': 2}])
print("Записано атомарно")