#streaming-json #no-std #json

no-std json-streaming

a library for reading and writing JSON from / to a stream without the need to materialize the data in memory. Provides both blocking and async APIs.

4 stable releases

Uses new Rust 2024

new 1.0.3 Sep 15, 2025
1.0.2 Sep 9, 2025
1.0.0 Sep 5, 2025

#497 in Web programming

Download history 188/week @ 2025-09-03 258/week @ 2025-09-10

446 downloads per month

Apache-2.0

310KB
5.5K SLoC

json-streaming - reading and writing JSON in a streaming fashion

This is a library for writing and reading JSON through a streaming API, i.e. without data structures that are then "mapped".

It fills a niche: for most typical use cases, the serde ecosystem is the better and more natural choice, and if serde does what you need, you should probably choose it over json-streaming. This library is written to cover special use cases:

  • Writing and processing big data structures without materializing them
  • Writing and reading JSON representations that have significant structural differences to the in-memory representation, e.g. flattening nested in-memory maps to a flat JSON object based on domain knowledge
  • Fine-grained control over how JSON is written
  • Working with JSON in a no-std, no-alloc environment

All APIs exist in both blocking and non-blocking variants.

For a change history and versions, see the Changelog.

Getting started

Here's a simple example of how to write JSON using the library:

use json_streaming::blocking::*;

fn write_something() -> std::io::Result<()> {
    let mut stdout = std::io::stdout();
    let mut writer = JsonWriter::new_pretty(&mut stdout);

    let mut o = JsonObject::new(&mut writer)?;
    o.write_string_value("a", "hello")?;
    o.write_string_value("b", "world")?;
    o.end()?;

    writer.flush()
}

Or reading the same data:

use json_streaming::blocking::*;

fn read_something(r: &mut impl io::Read) -> JsonParseResult<(), io::Error> {
    let mut json_reader = JsonReader::new(1024, r);

    json_reader.expect_start_object()?;
    loop {
        match json_reader.expect_key()? {
            Some("a") => println!("a: {}", json_reader.expect_string()?),
            Some("b") => println!("b: {}", json_reader.expect_string()?),
            Some(_other) => {
                return Err(JsonParseError::Parse("unexpected key parsing 'person'", json_reader.location()));
            },
            None => break,
        }
    }
    Ok(())
}

See the examples (blocking.rs and non_blocking.rs are good starting points) for more comprehensive code with lots of comments to explain.

Feature Flags

default

By default, both blocking and non-blocking APIs are included. Adapters to std::io::Read and std::io::Write are included, making the default library depend on std by default.

The tokio adapters for non-blocking APIs are not included and require the tokio feature flag. This is done to avoid pulling in the tokio dependency by default.

default = ["blocking", "non-blocking", "std"]

blocking and std

The blocking feature flag is active by default; for detailed control, default-features must be disabled. The blocking feature flag by itself adds the blocking APIs themselves (which do not depend on std), but not the adapters for std::io::Read and std::io::Write (which do).

So in order to work with JSON in a no-std environment, disable default-features and add the blocking feature flag. You will have to provide your own implementations of the BlockingRead or BlockingWrite trait to adapt to your environment's data sources or sinks. See the no_std.rs example for a showcase.

non-blocking and tokio

The non-blocking API is included by default, but without the adapters for Tokio's tokio::io::AsyncRead and tokio::io::AsyncWrite traits - those require the tokio feature flag, which adds a dependency on the Tokio library.

Dependencies

~2–12MB
~105K SLoC