jscan 0.1.0

High-performance zero-allocation JSON iterator and validator. Port of github.com/romshark/jscan/v2.
Documentation
  • Coverage
  • 76.67%
    23 out of 30 items documented1 out of 5 items with examples
  • Size
  • Source code size: 4.99 MB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 3.95 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 15s Average build duration of successful builds.
  • all releases: 15s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • mrzor/jscan-rs
    2 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • mrzor

jscan-rs

High-performance zero-allocation JSON iterator and validator for Rust.

A faithful port of romshark/jscan/v2 (Go).

What it does

jscan traverses JSON without deserializing it. Instead of building a DOM or mapping to structs, it calls your closure for every value it encounters — giving you the type, key, array index, raw value slice, nesting depth, and JSON Pointer path. Validation happens as a side effect.

This is useful when you need to:

  • Extract a few fields from large JSON without parsing the whole thing
  • Validate JSON syntax at high speed
  • Stream-process JSON values with minimal allocations
  • Build custom deserializers

Usage

use jscan::{scan, ValueType};

let json = br#"{"name":"Alice","scores":[98,76,100]}"#;

scan(json, |iter| {
    match iter.value_type() {
        ValueType::Number => {
            println!("{} = {}", iter.pointer(), std::str::from_utf8(iter.value()).unwrap());
        }
        _ => {}
    }
    false // return true to stop early
});
// Output:
// /scores/0 = 98
// /scores/1 = 76
// /scores/2 = 100

Validation only

use jscan::valid;

assert!(valid(br#"{"key": [1, 2, 3]}"#));
assert!(!valid(br#"{"trailing": 1,}"#));

Scanning multiple concatenated values

use jscan::scan_one;

let input = br#"123"hello"null"#;
let (rest, err) = scan_one(input, |iter| { /* ... */ false });
// rest = b#""hello"null"#, err = None

Reusable parser (avoids repeated allocation)

use jscan::Parser;

let mut parser = Parser::new(64);
for json_doc in &[br#""a""#.as_slice(), br#"[1,2]"#.as_slice()] {
    let err = parser.scan(json_doc, |iter| {
        // ...
        false
    });
}

Iterator API

Inside the callback, the Iterator provides:

Method Returns Description
value_type() ValueType Object, Array, String, Number, True, False, Null
value() &[u8] Raw value slice (empty for objects/arrays)
key() &[u8] Object member key including quotes (empty if not a member)
pointer() String RFC 6901 JSON Pointer path
level() usize Nesting depth (0 = root)
array_index() isize Element index in array, or -1
value_index() usize Byte offset of value start in source
value_index_end() isize Byte offset of value end, or -1 for containers
key_index() isize Byte offset of key start, or -1
key_index_end() isize Byte offset of key end, or -1
write_pointer(&mut buf) Write pointer to a reusable buffer (avoids alloc)

License

MIT — same as the original Go library.