Vyber vhodný model
DOM drží celý strom a zabere víc než velikost vstupních bajtů. SAX, StAX nebo pull parser čte události. XPath nad celým stromem a globální třídění mohou i tak vyžadovat paměť pro celý dokument.
XML po přímých potomcích v Pythonu
Ukázka předpokládá důvěryhodný dokument s kořenem katalog a knihami jako přímými potomky. Po zpracování odstraní knihu z rodiče; samotné clear by nechalo v rodiči odkazy na prázdné elementy. Pro namespace použij rozšířené jméno a pro nedůvěryhodné XML bezpečný parser a limity.
import xml.etree.ElementTree as ET
stack = []
for event, elem in ET.iterparse("katalog.xml", events=("start", "end")):
if event == "start":
stack.append(elem)
continue
if len(stack) == 2 and elem.tag == "kniha":
print(elem.get("id"), elem.findtext("nazev"))
stack[-2].remove(elem)
elem.clear()
stack.pop() Průběžné čtení a zápis JSON
JSON.parse načítá celý text a vytváří celou hodnotu. Pro velké pole potřebuješ parser, který čte a zpracovává data postupně; dělení na řádky není parser obecného JSON. Pokud kontroluješ formát exportu, použij záznamy po řádcích. Při zápisu čekej, dokud je výstup připraven přijmout další data. Tomuto řízení tempa se říká backpressure. Jinak se nezapsané záznamy hromadí v paměti.
Měř celý řetězec
Sleduj špičkovou paměť, čas a počet záznamů na reprezentativních datech. Náš online nástroj pracuje se vstupem v paměti prohlížeče; velké provozní exporty zpracuj lokálně. Limity nastav i pro jeden extrémně velký záznam, hluboký strom a komprimovaný vstup.
JSON Lines v Node.js s řízením rychlosti zápisu
import {createReadStream, createWriteStream} from "node:fs";
import {pipeline} from "node:stream/promises";
const MAX_LINE = 1024 * 1024;
await pipeline(
createReadStream("data.jsonl", {encoding:"utf8"}),
async function* (source) {
let pending = "";
let number = 0;
for await (const chunk of source) {
pending += chunk;
let index;
while ((index = pending.indexOf("\n")) !== -1) {
const line = pending.slice(0, index);
pending = pending.slice(index + 1);
number++;
if (Buffer.byteLength(line) > MAX_LINE) throw new Error(`Řádek ${number}: limit`);
if (line.trim() === "") throw new Error(`Řádek ${number}: prázdný záznam`);
let record;
try { record = JSON.parse(line); }
catch (error) { throw new Error(`Řádek ${number}: ${error.message}`); }
yield JSON.stringify(record) + "\n";
}
if (Buffer.byteLength(pending) > MAX_LINE) throw new Error("Limit rozpracovaného řádku");
}
if (pending !== "") throw new Error("Tento NDJSON kontrakt vyžaduje závěrečný newline");
},
createWriteStream("copy.jsonl", {encoding:"utf8"})
); Pipeline předává výstup podle kapacity zapisovače a zpracuje chyby jednotlivých fází. Generátor nezadržuje celé pole záznamů; pending má limit rozpracovaného řádku plus velikost právě přijatého bloku. Jedna JSON hodnota se stále parsuje celá. Kromě bajtů proto při citlivém provozu kontroluj i hloubku a strukturu.
Pro dva úvodní záznamy z JSON Lines vznikne copy.jsonl se dvěma kompaktními řádky. Chybný třetí řádek ukončí pipeline. Výstupní soubor může být částečný: aby příjemce neviděl nedokončený export, piš do dočasného souboru a přejmenuj jej až po úspěšném dokončení. Tento příklad vyžaduje jeden záznam na řádek a znak nového řádku i za posledním záznamem. Není určen pro čtení běžného JSON pole.
Jak měřit a navrhnout celý export
Nejdřív ověř počet a obsah záznamů na malé sadě. Potom zvyš počet řádově a sleduj špičkovou paměť, například lokálním měřením procesu nebo systémovým time s přepínačem pro paměť. Přesný příkaz závisí na systému: na macOS /usr/bin/time -l node stream-jsonl.mjs, na GNU systémech /usr/bin/time -v.
Průběžné čtení nepomůže, pokud výsledky uložíš do rostoucího pole, jedním krokem třídíš všechny záznamy nebo zapisovač neomezuje frontu. Zpracování s globálním pořadím navrhni s externím tříděním či databází. U komprimovaného vstupu omez také rozbalenou velikost. Dokumentaci pipeline a backpressure poskytuje Node.js streams.