JSON Toolkit

JSON Lines और NDJSON: streaming डेटा

मुख्य बात: प्रति पंक्ति एक JSON मान appendability, streaming और एक ही पंक्ति तक सीमित नुकसान दिलाता है; कीमत है समग्र structure का अभाव, इसलिए validation पंक्ति-दर-पंक्ति ही होनी पड़ती है।

मध्यम~10 मिनट पढ़ें2026-10-30 को प्रकाशित2026-10-30 को अपडेट किया गया
JSON Lines और NDJSON streaming डेटा का कवर

सार: जब आप पूरा batch एक साथ process करें तो JSON array इस्तेमाल करें, और जब streaming appends तथा ख़राब-पंक्ति isolation चाहिए तो JSON Lines; अगर आपको अब भी समग्र structure validate करना है, तो line-delimited format पर न जाएँ।

एक बार डेटा memory से बढ़ जाए, या लगातार आता रहे, तो "एक बड़ी array" की संरचना दुश्मन बन जाती है।

1. Format नियम

JSON Lines (.jsonl) और NDJSON (.ndjson) व्यवहार में समानार्थी हैं, और दोनों में नियम छोटे हैं:

  1. Encoding UTF-8 है, BOM के बिना;
  2. हर पंक्ति में एक पूर्ण, valid JSON मान होता है;
  3. पंक्तियाँ \n से अलग होती हैं; अंतिम पंक्ति newline पर खत्म हो या न हो;
  4. किसी पंक्ति के भीतर unescaped newline नहीं आ सकता — string के अंदर newline \n के रूप में लिखा जाना चाहिए;
  5. खाली पंक्तियाँ आमतौर पर नज़रअंदाज़ की जाती हैं, पर सख्त implementation उन्हें invalid माननी चाहिए।
{"time":"2026-10-30T10:41:45+08:00","level":"info","message":"boot"}
{"time":"2026-10-30T10:41:46+08:00","level":"warn","message":"slow query","costMs":812}
{"time":"2026-10-30T10:41:47+08:00","level":"error","message":"timeout","traceId":"a1b2"}

दोनों के बीच बारीक अंतर: JSON Lines website "प्रति पंक्ति एक मान, और मान कोई भी JSON हो सकता है (scalars सहित)" पर ज़ोर देती है, जबकि NDJSON specification "खाली पंक्तियाँ नहीं और top-level scalars नहीं" जैसे अधिक सख्त implementation नियम जोड़ती है। Engineering में, सख्त सेट अपनाने से आप मुसीबत से बचे रहते हैं।

2. JSON array के साथ तुलना

आयाम JSON array JSON Lines
एक record जोड़ना पूरी फ़ाइल rewrite करें (closing ] बदलें) बस एक पंक्ति जोड़ें
Streaming पढ़ना streaming parser चाहिए जो bracket depth ट्रैक करे पंक्ति से बाँटें
एक corrupt entry पूरी फ़ाइल unparsable हो जाती है केवल वही पंक्ति प्रभावित
Whole-file validation एक ही चरण में सब validate करें केवल पंक्ति-दर-पंक्ति
Compression gzip के बाद समान आकार वही, और blocks समानांतर में compress हो सकते हैं
Nesting व्यक्त करना मूल रूप से समर्थित हर पंक्ति भीतर nesting कर सकती है; पंक्तियाँ एक-दूसरे से असंबंधित
सामान्य उपभोक्ता सभी JSON उपकरण Log सिस्टम, big-data pipelines, LLM training sets

निष्कर्ष सीधा है: डेटा "records का batch" है या "एक document", यही तय करता है कि कौन-सा format इस्तेमाल करना है।

3. पढ़ना: पंक्ति-दर-पंक्ति streaming

Node में, सबसे सरल तरीका readline है:

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

/**
 * JSON Lines फ़ाइल को पंक्ति-दर-पंक्ति पढ़ता है और हर record के लिए callback बुलाता है।
 * @param filePath फ़ाइल का path
 * @param onRecord एक record के लिए handler
 * @returns सफल और विफल पंक्तियों की संख्या वाली statistics
 */
async function readJsonLines(
  filePath: string,
  onRecord: (record: unknown, lineNumber: number) => void,
): Promise<{ okCount: number; badCount: number }> {
  const lineReader = createInterface({
    input: createReadStream(filePath, { encoding: 'utf8' }),
    crlfDelay: Infinity,
  });

  let okCount = 0;
  let badCount = 0;

  for await (const rawLine of lineReader) {
    const line = rawLine.trim();
    if (!line) {
      continue;
    }
    try {
      onRecord(JSON.parse(line), okCount + badCount + 1);
      okCount += 1;
    } catch {
      badCount += 1;
    }
  }

  return { okCount, badCount };
}

इस कोड की memory खपत केवल एक पंक्ति के आकार पर निर्भर है, कुल फ़ाइल आकार पर नहीं — यही line-delimited format का सबसे बड़ा मूल्य है।

3.1 जब केवल कुछ fields चाहिए, पहले JSON.parse न करें

अगर हर record में 50 fields हैं और आपको केवल 3 चाहिए, तो JSON.parse सभी 50 के लिए objects बनाता है। Streaming parser (stream-json, clarinet) केवल लक्षित fields निकाल सकता है, कीमत अधिक implementation जटिलता है। सामान्य नियम: जब एक record कुछ सौ KB से कम हो, तो सीधे JSON.parse बुलाना आमतौर पर बेहतर सौदा है।

3.2 Chinese text और BOMs

Windows पर बनी .jsonl फ़ाइलें अक्सर BOM रखती हैं, और पहली पंक्ति पर JSON.parse गारंटीड रूप से fail होगा। पढ़ने से पहले BOM हटाएँ, या पहला chunk stream करते समय पहले तीन bytes जाँचें।

4. लिखना: appends और atomicity

import { appendFile } from 'node:fs/promises';

/**
 * एक JSON Lines record जोड़ता है।
 * @param filePath लक्ष्य फ़ाइल
 * @param record लिखने योग्य record object
 * @returns एक promise जो लिखाई पूरी होने पर resolve होता है
 */
async function appendRecord(filePath: string, record: unknown): Promise<void> {
  const line = `${JSON.stringify(record)}\n`;
  await appendFile(filePath, line, { encoding: 'utf8' });
}

दो बातें ध्यान देने योग्य:

  1. JSON.stringify के आउटपुट में newlines नहीं होते (जब तक किसी string का \n दो अक्षरों में escape न हुआ हो), इसलिए एक पंक्ति हमेशा ठीक एक record है;
  2. जब कई processes एक साथ append करते हैं, तो PIPE_BUF (Linux पर आमतौर पर 4096 bytes) से छोटा एक write atomic होता है; उससे बड़ा कुछ भी गारंटीड नहीं। उच्च-concurrency परिदृश्यों के लिए "प्रति process एक फ़ाइल, disk पर flush के बाद merge" अपनाएँ, या transactions वाला storage इस्तेमाल करें।

5. ख़राब पंक्तियाँ संभालना

यह वह निर्णय है जो line-delimited format आपको पहले से लेना मजबूर करता है:

रणनीति किसके लिए उपयुक्त लागत
छोड़ें और गिनें ऐसा डेटा जो gaps सहन करता है, जैसे logs और analytics events डेटा चुपचाप खोता है, इसलिए alerting अनिवार्य
तुरंत fail करें ऐसा डेटा जो खोना नहीं चाहिए, जैसे billing और invoices एक ख़राब पंक्ति पूरा batch रोक देती है
Dead-letter फ़ाइल में भेजें ऐसे मामले जिन्हें बाद में backfill करना है अतिरिक्त storage और replay process

जो भी चुनें, पंक्ति संख्या और मूल सामग्री का एक अंश दर्ज करें, वरना बाद में समस्या ढूँढ नहीं पाएँगे। JSON.parse error संदेश में पंक्ति संख्या नहीं होती, इसलिए आपको उसे खुद बनाए रखना होता है।

6. Compression और sharding

  • gzip .jsonl को लगातार उच्च अनुपात में compress करता है (field नाम दोहराए जाते हैं), इसलिए .jsonl.gz सुझाया जाता है;
  • gzip blocks में compress करता है और मूल रूप से streaming decompression समर्थित करता है, इसलिए पूरी फ़ाइल decompress करने की ज़रूरत नहीं;
  • बड़ी फ़ाइलों को लगभग 1GB के slices में बाँटें, नामों में sequence numbers रखें, ताकि parallel processing और resumable transfer आसान हो;
  • .jsonl को "array में merge" करके कभी preprocess न करें — यह उसके सारे लाभ फेंक देता है।

7. अन्य big-data formats के साथ समझौते

Format Structure Schema शामिल किसके लिए अच्छा
JSON Lines प्रति पंक्ति स्वतंत्र नहीं (बाहरी अनुबंध चाहिए) Logs, imports और exports, training data
CSV Tabular नहीं Reports, Excel interchange
Parquet Columnar हाँ Analytical queries, बहुत बड़े offline datasets
Avro Row-based हाँ Kafka messages, schema evolution

JSON Lines का लाभ यह है कि कोई भी भाषा और कोई भी उपकरण उसे तुरंत पढ़ और लिख सकता है; कमी schema और columnar compression का अभाव है। एक बार डेटा एक मशीन पर फ़िट न हो और आपको बार-बार column-oriented queries चाहिए, तो Parquet पर विचार करने का समय आ जाता है।

8. जाँच सूची

  • [ ] फ़ाइल extension संगत है (.jsonl या .ndjson) और कभी मिलाया नहीं गया;
  • [ ] Writer "प्रति पंक्ति एक record" की गारंटी देता है, strings के भीतर newlines पहले से escaped हैं;
  • [ ] Reader BOM हटाता है, खाली पंक्तियाँ छोड़ता है, और पंक्ति संख्याएँ बनाए रखता है;
  • [ ] ख़राब-पंक्ति रणनीति स्पष्ट रूप से परिभाषित और निगरानी योग्य है (ख़राब पंक्तियाँ / कुल पंक्तियाँ);
  • [ ] बड़ी फ़ाइलें आकार के अनुसार sliced हैं और gzip व parallel reads के साथ जोड़ी गई हैं;
  • [ ] जहाँ field अनुबंध मौजूद है, वहाँ अतिरिक्त schema दिया गया है और पंक्ति-दर-पंक्ति validate होता है।

सामान्य प्रश्न

JSON array और JSON Lines में कैसे चुनें?

देखें कि डेटा records का batch है या एक document। जब records append होते हैं और stream के रूप में पढ़े जाते हैं तो JSON Lines चुनें: appending का अर्थ है अंत में एक पंक्ति लिखना, और पढ़ने के लिए केवल पंक्ति से बाँटना। जब पूरा structure एक ही चरण में validate करना हो तो JSON array रखें, क्योंकि JSON array सब कुछ एक साथ validate करता है जबकि JSON Lines केवल पंक्ति-दर-पंक्ति validate हो सकता है।

एक corrupt पंक्ति पूरी फ़ाइल क्यों नहीं तोड़ती?

JSON Lines में हर पंक्ति एक स्वतंत्र, पूर्ण JSON मान है, और parsing स्वाभाविक रूप से पंक्ति-दर-पंक्ति होती है, इसलिए corrupt डेटा केवल उसी पंक्ति को प्रभावित करता है। JSON array में एक syntax error पूरी फ़ाइल को unparsable बना देता है, इसीलिए line-delimited डेटा logs और analytics events के लिए उपयुक्त है।

क्या JSON Lines को JSON array में बदलना चाहिए?

कर सकते हैं: पंक्ति-दर-पंक्ति पढ़ें, हर record पर JSON.parse बुलाएँ, और उन्हें array में इकट्ठा करें। ऐसा करने के लिए पूरी सामग्री memory में फ़िट होनी चाहिए और इससे streaming reads व one-line appends छूट जाते हैं। .jsonl को array में preprocess करना format के सारे लाभ फेंक देता है, इसलिए यह केवल एकबारगी delivery के लिए उचित है जहाँ डेटा memory में फ़िट हो।

संदर्भ

मेल खाते उपकरण

संबंधित लेख

JSON5 और JSONC: comments वाला JSON

दोनों dialects इसलिए हैं क्योंकि मनुष्यों द्वारा लिखी config को comments और trailing commas चाहिए, पर उनका syntax और पारिस्थितिकी तंत्र अलग है, इसलिए गलत चुनाव आपकी toolchain को फँसा देता है।

शुरुआती~10 मिनट पढ़ें2026-10-30 को अपडेट किया गया

बड़ी JSON का पार्सिंग, रेंडरिंग और स्ट्रीमिंग

पार्सिंग अक्सर अड़चन नहीं होती; रेंडरिंग और कॉपी होती हैं। "पार्स → ट्रैवर्स → रेंडर → ट्रांसपोर्ट" के चार स्तरों पर अनुकूलन करें ताकि आपका प्रयास व्यर्थ न जाए।

मध्यम~13 मिनट पढ़ें2026-10-31 को अपडेट किया गया

JSONL के साथ व्यावहारिक कार्य: खोलना, रूपांतरण और खराब लाइनों की पहचान

पूरी .jsonl फ़ाइल को JSON.parse को देकर त्रुटि पाना लगभग कभी यह नहीं दर्शाता कि फ़ाइल खराब है — यह दर्शाता है कि पढ़ने की रणनीति गलत है। यह मार्गदर्शिका JSONL की जाँच और लाइन-दर-लाइन पढ़ने के मानक तरीके, JSON ऐरे के साथ दोनों दिशाओं में रूपांतरण की कमांड, छह प्रकार की खराब लाइनों की तालिका और स्ट्रीमिंग HTTP प्रतिक्रियाओं से NDJSON उपभोग का सही तरीका बताती है।

मध्यम~9 मिनट पढ़ें2026-11-06 को अपडेट किया गया