Crate actson

source ·
Expand description

§Actson

A non-blocking, event-based JSON parser.

§Examples

§Push-based parsing

Push-based parsing is the most flexible way of using Actson. Push new bytes into a PushJsonFeeder and then let the parser consume them until it returns Some(JsonEvent::NeedMoreInput). Repeat this process until you receive None, which means the end of the JSON text has been reached. The parser returns Err if the JSON text is invalid or some other error has occurred.

This approach is very low-level but gives you the freedom to provide new bytes to the parser whenever they are available and to generate new JSON events whenever you need them.

use actson::{JsonParser, JsonEvent};
use actson::feeder::{PushJsonFeeder, JsonFeeder};

let json = r#"{"name": "Elvis"}"#.as_bytes();

let feeder = PushJsonFeeder::new();
let mut parser = JsonParser::new(feeder);
let mut i = 0;
while let Some(event) = parser.next_event().unwrap() {
    match event {
        JsonEvent::NeedMoreInput => {
            // feed as many bytes as possible to the parser
            i += parser.feeder.push_bytes(&json[i..]);
            if i == json.len() {
                parser.feeder.done();
            }
        }

        JsonEvent::FieldName => assert!(matches!(parser.current_str(), Ok("name"))),
        JsonEvent::ValueString => assert!(matches!(parser.current_str(), Ok("Elvis"))),

        _ => {} // there are many other event types you may process here
    }
}

§Asynchronous parsing with Tokio

Actson can be used with Tokio to parse JSON asynchronously.

The main idea here is to call JsonParser::next_event() in a loop to parse the JSON document and to produce events. Whenever you get JsonEvent::NeedMoreInput, call AsyncBufReaderJsonFeeder::fill_buf() to asynchronously read more bytes from the input and to provide them to the parser.

Heads up: The tokio feature has to be enabled for this. It is disabled by default.

use tokio::fs::File;
use tokio::io::{self, AsyncReadExt, BufReader};

use actson::{JsonParser, JsonEvent};
use actson::tokio::AsyncBufReaderJsonFeeder;

#[tokio::main]
async fn main() {
    let file = File::open("tests/fixtures/pass1.txt").await.unwrap();
    let reader = BufReader::new(file);

    let feeder = AsyncBufReaderJsonFeeder::new(reader);
    let mut parser = JsonParser::new(feeder);
    while let Some(event) = parser.next_event().unwrap() {
        match event {
            JsonEvent::NeedMoreInput => parser.feeder.fill_buf().await.unwrap(),
            _ => {} // do something useful with the event
        }
    }
}

§Parsing from a BufReader

BufReaderJsonFeeder allows you to feed the parser from a BufReader.

Note: By following this synchronous and blocking approach, you are missing out on Actson’s reactive properties. We recommend using Actson together with Tokio instead to parse JSON asynchronously (see above).

use actson::{JsonParser, JsonEvent};
use actson::feeder::BufReaderJsonFeeder;

use std::fs::File;
use std::io::BufReader;

let file = File::open("tests/fixtures/pass1.txt").unwrap();
let reader = BufReader::new(file);

let feeder = BufReaderJsonFeeder::new(reader);
let mut parser = JsonParser::new(feeder);
while let Some(event) = parser.next_event().unwrap() {
    match event {
        JsonEvent::NeedMoreInput => parser.feeder.fill_buf().unwrap(),
        _ => {} // do something useful with the event
    }
}

§Parsing a slice of bytes

For convenience, SliceJsonFeeder allows you to feed the parser from a slice of bytes.

use actson::{JsonParser, JsonEvent};
use actson::feeder::SliceJsonFeeder;

let json = r#"{"name": "Elvis"}"#.as_bytes();

let feeder = SliceJsonFeeder::new(json);
let mut parser = JsonParser::new(feeder);
while let Some(event) = parser.next_event().unwrap() {
    match event {
        JsonEvent::FieldName => assert!(matches!(parser.current_str(), Ok("name"))),
        JsonEvent::ValueString => assert!(matches!(parser.current_str(), Ok("Elvis"))),
        _ => {}
    }
}

§Parsing into a Serde JSON Value

For testing and compatibility reasons, Actson is able to parse a byte slice into a Serde JSON Value.

Heads up: You need to enable the serde_json feature for this.

use actson::serde_json::from_slice;

let json = r#"{"name": "Elvis"}"#.as_bytes();
let value = from_slice(json).unwrap();

assert!(value.is_object());
assert_eq!(value["name"], "Elvis");

However, if you find yourself doing this, you probably don’t need the reactive features of Actson and your data seems to completely fit into memory. In this case, you’re most likely better off using Serde JSON directly.

Re-exports§

Modules§