jsonparse and stream-json are both Node.js libraries designed for parsing JSON data incrementally as it arrives through streams, rather than loading the entire payload into memory. This is essential when handling large JSON files or real-time data feeds where memory efficiency and low latency matter. jsonparse provides a low-level, event-driven SAX-like parser that emits tokens as it encounters them. stream-json builds on similar principles but offers a more structured streaming pipeline with filters, value extraction, and object assembly capabilities out of the box.
When dealing with large JSON payloads — think multi-gigabyte log dumps, real-time telemetry feeds, or massive API responses — loading everything into memory with JSON.parse() isn’t feasible. Both jsonparse and stream-json solve this by parsing JSON as it streams in, using minimal memory. But they take very different approaches. Let’s compare how they work in practice.
jsonparse is a low-level SAX-style tokenizer. It reads a stream and emits raw parsing events like "key", "string", "number", "startObject", etc. You’re responsible for tracking nesting depth and reconstructing meaningful data structures.
// jsonparse: manual token handling
const JSONStream = require('jsonparse');
const parser = new JSONStream();
let currentKey = null;
let inTargetObject = false;
parser.onValue = (value) => {
if (inTargetObject && currentKey === 'email') {
console.log('Found email:', value);
}
};
parser.onToken = (token, value) => {
if (token === 'key') {
currentKey = value;
} else if (token === 'startObject') {
// Track object depth manually if needed
}
};
process.stdin.pipe(parser);
stream-json treats parsing as a composable pipeline. It provides stream transformers like StreamArray (for top-level arrays), Filter (to keep only certain paths), and WithParser (which wraps jsonparse internally). You chain these together like Unix pipes.
// stream-json: declarative pipeline
const { streamArray } = require('stream-json/streamers/StreamArray');
const { filter } = require('stream-json/filters/Filter');
process.stdin
.pipe(streamArray()) // breaks [ {...}, {...} ] into individual objects
.pipe(filter({ path: ['*', 'email'] })) // only pass items with an 'email' field
.on('data', ({ value }) => {
console.log('Found email:', value.email);
});
Parsing flat JSON is easy. The real test is navigating deep, nested objects without loading everything.
jsonparse forces you to track state manually. For example, to extract user.profile.email, you’d maintain a stack of current keys and check the path on every token.
// jsonparse: manual path tracking
const parser = new (require('jsonparse'))();
const path = [];
parser.onToken = (token, value) => {
if (token === 'key') {
path.push(value);
} else if (token === 'endObject' || token === 'endArray') {
path.pop();
}
};
parser.onValue = (value) => {
if (path.join('.') === 'user.profile.email') {
console.log('Email:', value);
}
};
stream-json handles paths declaratively. Its Filter and Pick utilities understand JSON paths natively, so you just specify what you want.
// stream-json: built-in path support
const { withParser } = require('stream-json');
const { pick } = require('stream-json/filters/Pick');
process.stdin
.pipe(withParser())
.pipe(pick({ filter: 'user.profile.email' }))
.on('data', ({ value }) => {
console.log('Email:', value);
});
Both libraries stop parsing on malformed JSON, but their error reporting differs.
jsonparse throws errors directly on the stream. You must attach an 'error' listener to avoid crashing your process.
// jsonparse: basic error handling
const parser = new (require('jsonparse'))();
parser.on('error', (err) => {
console.error('Parse error:', err.message);
});
stream-json follows standard Node.js stream error conventions. Errors propagate through the pipeline, so you can handle them at the end.
// stream-json: pipeline error handling
const pipeline = require('stream').pipeline;
pipeline(
process.stdin,
streamArray(),
(err) => {
if (err) console.error('Stream failed:', err.message);
}
);
You only need the id and email fields.
jsonparse: Possible but tedious. You’d track whether you’re inside a user object, then capture those two keys.stream-json: Clean and concise:const { streamArray } = require('stream-json/streamers/StreamArray');
const { pick } = require('stream-json/filters/Pick');
fs.createReadStream('huge-users.json')
.pipe(streamArray())
.pipe(pick({ filter: ['id', 'email'] }))
.on('data', ({ value }) => {
// value = { id: 123, email: '...' }
});
You’re receiving JSON line-by-line and need to reject invalid entries immediately.
jsonparse: Better fit here because you get immediate token feedback and can reset the parser per line.stream-json: Designed for single JSON documents, not NDJSON (newline-delimited). You’d need extra glue code.As of 2024, jsonparse is stable but minimally maintained. Its API hasn’t changed in years because it solves one problem well: tokenizing JSON streams. No deprecation notices exist, but don’t expect new features.
stream-json is actively maintained with regular updates. It’s part of a broader ecosystem (stream-chain, stream-object) and integrates cleanly with modern Node.js streams (including async iterators).
| Feature | jsonparse | stream-json |
|---|---|---|
| Abstraction Level | Low-level tokenizer | High-level streaming pipeline |
| Path Support | Manual tracking required | Built-in (Filter, Pick) |
| Top-Level Array Handling | Not built-in | StreamArray utility included |
| Error Propagation | Direct error events | Standard Node.js stream errors |
| Best For | Custom parsers, tight memory constraints | Extracting fields from large JSON files |
Reach for jsonparse when you’re building infrastructure — like a custom JSON validator, a protocol decoder, or when every byte of memory counts. You’ll trade convenience for control.
Choose stream-json for application-level data processing — like ETL jobs, log analysis, or API integrations where you need to pluck values from huge JSON responses. It saves you from reinventing stream composition.
Both avoid the memory pitfalls of JSON.parse(), but stream-json gets you to a working solution faster for most real-world tasks, while jsonparse gives you the raw materials to build something specialized.
Choose jsonparse if you need a minimal, fast, and low-level streaming JSON tokenizer that gives you direct access to parsing events (like key, value, start/end of objects/arrays). It’s ideal when you’re building a custom parser or need fine-grained control over token handling without extra abstractions. However, be prepared to manage state manually for reconstructing complex structures.
Choose stream-json if you want a higher-level, composable streaming toolkit that includes utilities for filtering keys, assembling partial objects, and extracting values without buffering the full JSON. It’s better suited for real-world data processing tasks like parsing gigabyte-sized JSON logs or API responses where you only care about specific fields.
This is a streaming JSON parser. For a simpler, sax-based version see this gist: https://gist.github.com/1821394
The MIT License (MIT) Copyright (c) 2011-2012 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.