JSON Lines (NDJSON): JSON po řádcích

JSON Lines ukládá jednu platnou JSON hodnotu na každý řádek v UTF-8. Hodí se pro streamové exporty a logy; celý soubor obvykle není jediný JSON text.

Autor: Aktualizováno

Dva záznamy

Mezi řádky není čárka ani obalující pole. Prázdný řádek není platná JSON hodnota. Řetězec může obsahovat escape \n, nikoli doslovné zalomení uvnitř jedné hodnoty.

Dva záznamy
{"id":1,"nazev":"XML"}
{"id":2,"nazev":"JSON"}

Čtení po řádcích

Ukázka zpracuje každý záznam a u chyby přidá číslo řádku. JSON Lines dovoluje libovolnou JSON hodnotu; aplikace může vyžadovat jen objekty. Velikost jednotlivého řádku omez při čtení, nejen po načtení.

Čtení po řádcích
import json
with open("data.jsonl", encoding="utf-8") as source:
    for number, line in enumerate(source, 1):
        try:
            record = json.loads(line)
        except json.JSONDecodeError as error:
            raise ValueError(f"Řádek {number}: {error}") from error
        print(record)

Konvence a přenos

JSON Lines není RFC. Příbuzné NDJSON definuje samostatný popis. Přípony a MIME typy nejsou univerzálně sjednocené, proto je dohodni s příjemcem. JSON Lines nepovoluje BOM; finální nový řádek je doporučený.

Nástroje

Jq běžně načítá posloupnost JSON hodnot; jq -c . data.jsonl vypíše každou kompaktně. Přepínač -s je shromáždí do pole a vrátí paměťové nároky celé sady. Online JSON validátor čeká jednu hodnotu, takže testuj jednotlivý řádek.

Čtení s limitem jednoho řádku a číslem chyby

read-jsonl.py — zde aplikace požaduje objekty, nikoli libovolné JSON hodnoty
import json
MAX_LINE_BYTES = 1024 * 1024
with open("data.jsonl", "rb") as source:
    number = 0
    while True:
        raw = source.readline(MAX_LINE_BYTES + 1)
        if not raw:
            break
        number += 1
        if len(raw) > MAX_LINE_BYTES:
            raise ValueError(f"Řádek {number}: příliš velký záznam")
        try:
            record = json.loads(raw.decode("utf-8"))
        except (UnicodeDecodeError, json.JSONDecodeError) as error:
            raise ValueError(f"Řádek {number}: {error}") from error
        if not isinstance(record, dict):
            raise ValueError(f"Řádek {number}: očekáván objekt")
        print(record["id"], record["nazev"])

Ulož úvodní dva záznamy do data.jsonl a spusť python3 read-jsonl.py. Výstup je 1 XML a 2 JSON. Limit zahrnuje i případné koncové bajty řádku. Zpracování po řádcích brání načtení celého souboru, ale jeden extrémně dlouhý řádek musí být omezen už při čtení.

Přidej prázdný třetí řádek: program nahlásí syntaktickou chybu s číslem 3. Vlož na třetí řádek hodnotu null: syntax projde, ale aplikační kontrola odmítne očekávaný objekt. Chybějící id je další samostatná porušení pravidel pro strukturu záznamu; místo přímého přístupu můžeš použít schéma každého záznamu.

Zápis a vztah k NDJSON

write-jsonl.py — jeden kompaktní JSON text na řádek
import json
records = [{"id": 1, "nazev": "XML"}, {"id": 2, "nazev": "JSON"}]
with open("export.jsonl", "w", encoding="utf-8", newline="\n") as output:
    for record in records:
        output.write(json.dumps(record, ensure_ascii=False, allow_nan=False) + "\n")

Nepoužívej vícerádkové indentování jednotlivého objektu. Znak nového řádku uvnitř textové hodnoty serializátor zapíše escapovaně, aby nerozdělil záznam. Parametr allow_nan=False odmítne hodnoty, které nejsou standardní JSON čísla. Pro skutečně velký export musí také zdroj poskytovat záznamy v records postupně, nikoli držet celý seznam v paměti.

JSON Lines a NDJSON jsou blízké konvence, ale dohodni přísnost vůči prázdným řádkům, finálnímu newline a transportnímu typu. NDJSON předepisuje ukončení jednotlivých JSON textů newline a popisuje zacházení s prázdnými řádky. Celý soubor není jedno obalující JSON pole. Převod na pole přidá hranaté závorky a čárky mezi záznamy; neprováděj jej slepým slepením řádků.