9 releases
| new 0.2.2 | Jan 17, 2026 |
|---|---|
| 0.2.1 | Jul 16, 2025 |
| 0.1.5 | Jul 12, 2025 |
| 0.1.2 | Jun 30, 2025 |
#165 in Embedded development
405KB
9K
SLoC
picojson
A minimal Rust JSON pull-parser for resource-constrained environments.
picojson provides a low-level, no_std compatible pull-parser that operates without recursion or heap allocations. It is designed for scenarios where memory is limited and direct control over parsing is required, such as embedded systems.
Features
- Pull-style Parsing: Process JSON from byte slices (
SliceParser) or any source that implements aReadertrait (StreamParser). - Zero Allocations: The parser does not perform any heap allocations. All memory, including an optional scratch buffer for value copying, is provided by the caller.
- No Recursion: The parsing logic is implemented with an iterative loop, ensuring a predictable and flat call stack.
no_stdby Default: Designed for bare-metal and embedded use cases.- Configurable Tree Depth: The maximum JSON nesting depth is configured by the user at compile time to control stack usage.
- Configurable Number Handling: Integer width and float parsing behavior are configurable via feature flags.
- Unsafe-Free: The crate contains no
unsafecode. - Panic-Free: Does not panic.
Design Philosophy
The core of picojson is a minimal, non-recursive tokenizer that uses a bitstack (1 bit per nesting level) to track object/array depth. The parsers build upon this to provide a higher-level event stream.
The design prioritizes a small resource footprint and predictable deterministic execution over speed.
Value Handling: No copy and Copy-on-Write
-
By default, the parser returns values (strings, keys, numbers) as borrowed slices of the original input.
-
In certain situations, a value must be copied into a user-provided scratch buffer:
- String Escapes: If a string or key contains escape sequences (e.g.,
\n,\u0041), its content must be un-escaped into the scratch buffer. - Stream Buffering: When using the
StreamParser, if a token (like a long number) is split across separate reads from the underlying I/O source, it must be copied into the scratch buffer to be made contiguous.
- String Escapes: If a string or key contains escape sequences (e.g.,
Usage
Parsing from a Slice
Use SliceParser when the entire JSON document is in memory. A scratch buffer is required to handle potential string escapes.
use picojson::{SliceParser, Event, String};
let json = r#"{"message": "Hello\nWorld"}"#;
let mut scratch = [0u8; 32];
let mut parser = SliceParser::with_buffer(json, &mut scratch);
loop {
match parser.next_event()? {
Event::Key(key) => { // key is a picojson::String
println!("Key: {}", key);
}
Event::String(value) => {
println!("Value: {}", value);
}
Event::EndDocument => break,
_ => {}
}
}
Parsing from a Stream
Use StreamParser for parsing from any source that implements the Reader trait, such as a file or network socket.
use picojson::{StreamParser, Event, Reader};
// A simple Reader implementation over a byte slice.
struct MyReader<'a> {
data: &'a [u8],
pos: usize,
}
// ... Reader implementation ...
let json_stream = b"{\"id\": 123}";
let mut buffer = [0u8; 1024]; // Buffer for the parser to use.
let mut parser = StreamParser::new(MyReader::new(json_stream), &mut buffer);
// ... event loop ...
See the API docs and examples for more details. Some test results can be found on the project site.
Configuration
The parser's behavior can be customized with feature flags. For example, integer width can be set to int32 or int64, and float handling can be configured to error, truncate, or ignore. For a detailed guide, please see the crate reference documentation.
Stability
This library is experimental, though the parsers pass conformance tests.
For battle-tested code please use serde-json-core or various other alternatives.
License
Apache 2.0; see LICENSE for details.
No runtime deps
~180KB