Skip to content

Usage Guide v2.0.0

This guide provides in-depth examples for using vexy_json v2.0.0 in Rust and JavaScript/WebAssembly, including the new streaming API, parallel processing, and plugin system.

Basic Parsing (Rust)

The simplest way to use vexy_json is with the parse function:

use vexy_json::parse;

fn main() {
    let json_data = r#"{ key: "value", num: 123, // comment\n trailing: [1,2,3,], hex: 0xFF }"#;
    let value = parse(json_data).unwrap();
    println!("{:?}", value);
}

Customizing Parsing with ParserOptions

For more control, use parse_with_options and configure ParserOptions:

use vexy_json::{parse_with_options, ParserOptions};

fn main() {
    let input = "a:1, b:2";
    let options = ParserOptions {
        allow_comments: true,
        allow_unquoted_keys: true,
        allow_trailing_commas: true,
        allow_implicit_top_level: true,
        allow_newline_as_comma: true,
        allow_single_quoted_strings: true,
        allow_extended_numbers: true,
        ..Default::default()
    };
    let value = parse_with_options(input, &options).unwrap();
    println!("{:?}", value);
}

WebAssembly/JavaScript Usage

See docs/wasm.md for full API details.

import init, { parse_json_with_options } from './pkg/vexy_json_wasm.js';

await init();
const result = parse_json_with_options('{a:1}', { allow_comments: true });
console.log(result); // { a: 1 }

Customizing Parsing with ParserOptions

For more control over the parsing behavior, you can use parse_with_options and configure ParserOptions.

use vexy_json::{parse_with_options, ParserOptions};

fn main() {
    // Example: Strict JSON parsing (disabling all forgiving features)
    let mut strict_options = ParserOptions::default();
    strict_options.allow_comments = false;
    strict_options.allow_trailing_commas = false;
    strict_options.allow_unquoted_keys = false;
    strict_options.allow_single_quotes = false;
    strict_options.implicit_top_level = false;
    strict_options.newline_as_comma = false;

    let strict_json = r#"{"key": "value"}"#;
    match parse_with_options(strict_json, strict_options) {
        Ok(value) => println!("Parsed strictly: {:?}", value),
        Err(e) => eprintln!("Strict parsing error: {}", e),
    }

    // Example: Allowing only unquoted keys and implicit top-level
    let mut custom_options = ParserOptions::default();
    custom_options.allow_unquoted_keys = true;
    custom_options.implicit_top_level = true;
    custom_options.allow_comments = false; // Keep other defaults or explicitly set

    let custom_json = r#"myKey: "myValue", another: 42"#;
    match parse_with_options(custom_json, custom_options) {
        Ok(value) => println!("Parsed with custom options: {:?}", value),
        Err(e) => eprintln!("Custom parsing error: {}", e),
    }
}

Handling Forgiving Features

vexy_json excels at parsing JSON with common relaxations. Here are examples of how it handles them:

Comments

Both single-line (//, #) and multi-line (/* ... */) comments are ignored.

use vexy_json::parse;

fn main() {
    let json_with_comments = r#"
        {
            // This is a single-line comment
            "name": "Alice", /* This is a
                                multi-line comment */
            "age": 30, # Another comment style
        }
    "#;
    let value = parse(json_with_comments).unwrap();
    println!("Parsed with comments: {:?}", value);
}

Trailing Commas

Trailing commas in arrays and objects are gracefully handled.

use vexy_json::parse;

fn main() {
    let json_with_trailing_comma = r#"
        [
            1,
            2,
            3, // Trailing comma here
        ]
    "#;
    let value = parse(json_with_trailing_comma).unwrap();
    println!("Parsed with trailing comma: {:#?}", value);

    let obj_with_trailing_comma = r#"
        {
            key1: "value1",
            key2: "value2", // Trailing comma here
        }
    "#;
    let obj_value = parse(obj_with_trailing_comma).unwrap();
    println!("Parsed object with trailing comma: {:#?}", obj_value);
}

Unquoted Keys

Object keys do not need to be quoted, as long as they are valid identifiers.

use vexy_json::parse;

fn main() {
    let json_unquoted_keys = r#"{ firstName: "John", lastName: "Doe" }"#;
    let value = parse(json_unquoted_keys).unwrap();
    println!("Parsed with unquoted keys: {:#?}", value);
}

Implicit Top-Level Objects and Arrays

You don't need to wrap your entire input in {} or [] if it's clearly an object or an array.

use vexy_json::parse;

fn main() {
    // Implicit object
    let implicit_obj = r#"name: "Bob", age: 25"#;
    let obj_value = parse(implicit_obj).unwrap();
    println!("Parsed implicit object: {:#?}", obj_value);

    // Implicit array
    let implicit_arr = r#""apple", "banana", "cherry""#;
    let arr_value = parse(implicit_arr).unwrap();
    println!("Parsed implicit array: {:#?}", arr_value);
}

Newline as Comma

When the newline_as_comma option is enabled, newlines can act as implicit comma separators.

use vexy_json::{parse_with_options, ParserOptions};

fn main() {
    let mut options = ParserOptions::default();
    options.newline_as_comma = true;

    let json_with_newlines = r#"
        [
            1
            2
            3
        ]
    "#;
    let value = parse_with_options(json_with_newlines, options).unwrap();
    println!("Parsed with newlines as commas: {:#?}", value);

    let obj_with_newlines = r#"
        {
            key1: "value1"
            key2: "value2"
        }
    "#;
    let obj_value = parse_with_options(obj_with_newlines, options).unwrap();
    println!("Parsed object with newlines as commas: {:#?}", obj_value);
}

Error Handling

vexy_json returns a Result<Value, Error> which allows for robust error handling. You should always check the Result to handle potential parsing issues.

use vexy_json::parse;

fn main() {
    let invalid_json = r#"{ key: "value }"#; // Missing closing quote
    match parse(invalid_json) {
        Ok(value) => println!("Parsed: {:?}", value),
        Err(e) => eprintln!("Parsing error: {}", e),
    }
}

For more details on error types, refer to the API Reference.

Streaming API Usage (New in v2.0.0)

The streaming API is ideal for processing large JSON files without loading them entirely into memory.

Basic Streaming Example

use vexy_json::{StreamingParser, StreamingEvent};

fn process_large_file(json_content: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut parser = StreamingParser::new();
    parser.feed(json_content)?;
    parser.finish()?;

    let mut depth = 0;
    while let Some(event) = parser.next_event()? {
        match event {
            StreamingEvent::StartObject => {
                println!("{:indent$}Object {", "", indent = depth * 2);
                depth += 1;
            }
            StreamingEvent::EndObject => {
                depth -= 1;
                println!("{:indent$}}}", "", indent = depth * 2);
            }
            StreamingEvent::ObjectKey(key) => {
                print!("{:indent$}{}: ", "", key, indent = depth * 2);
            }
            StreamingEvent::String(s) => println!("\"{}\"", s),
            StreamingEvent::Number(n) => println!("{}", n),
            StreamingEvent::Bool(b) => println!("{}", b),
            StreamingEvent::Null => println!("null"),
            StreamingEvent::EndOfInput => break,
            _ => {}
        }
    }
    Ok(())
}

Incremental Parsing

Perfect for network streams or reading files in chunks:

use vexy_json::StreamingParser;
use std::io::{BufReader, BufRead};
use std::fs::File;

fn parse_file_incrementally(path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let mut parser = StreamingParser::new();

    for line in reader.lines() {
        parser.feed(&line?)?;

        // Process available events after each line
        while let Some(event) = parser.next_event()? {
            // Handle events...
        }
    }

    parser.finish()?;
    Ok(())
}

Parallel Processing (New in v2.0.0)

Process multiple JSON files or strings in parallel for improved performance.

Basic Parallel Parsing

use vexy_json::{parse_parallel, ParallelOptions};
use std::fs;

fn process_json_files(directory: &str) -> Result<(), Box<dyn std::error::Error>> {
    let files: Vec<String> = fs::read_dir(directory)?
        .filter_map(|entry| {
            entry.ok().and_then(|e| {
                let path = e.path();
                if path.extension()? == "json" {
                    fs::read_to_string(path).ok()
                } else {
                    None
                }
            })
        })
        .collect();

    let results = parse_parallel(files);

    for (i, result) in results.iter().enumerate() {
        match result {
            Ok(value) => println!("File {} parsed successfully", i),
            Err(e) => eprintln!("Error in file {}: {}", i, e),
        }
    }

    Ok(())
}

Custom Parallel Options

use vexy_json::{parse_parallel_with_options, ParallelOptions, ParserOptions};

let mut parallel_opts = ParallelOptions::default();
parallel_opts.num_threads = Some(8);  // Use 8 threads
parallel_opts.chunk_size = Some(100); // Process 100 items per chunk

let mut parser_opts = ParserOptions::default();
parser_opts.allow_comments = true;
parser_opts.allow_trailing_commas = true;

parallel_opts.parser_options = parser_opts;

let results = parse_parallel_with_options(json_strings, parallel_opts);

Plugin System (New in v2.0.0)

Extend vexy_json with custom functionality through plugins.

Creating a Custom Plugin

use vexy_json::{Plugin, Value, Error};
use std::collections::HashMap;

// Plugin to redact sensitive information
struct RedactPlugin {
    sensitive_keys: Vec<String>,
}

impl Plugin for RedactPlugin {
    fn name(&self) -> &str {
        "redact-sensitive"
    }

    fn transform(&self, value: &mut Value) -> Result<(), Error> {
        match value {
            Value::Object(map) => {
                for key in &self.sensitive_keys {
                    if map.contains_key(key) {
                        map.insert(key.clone(), Value::String("[REDACTED]".to_string()));
                    }
                }
                // Recursively process nested objects
                for (_, v) in map.iter_mut() {
                    self.transform(v)?;
                }
            }
            Value::Array(arr) => {
                for v in arr.iter_mut() {
                    self.transform(v)?;
                }
            }
            _ => {}
        }
        Ok(())
    }
}

// Usage
let plugin = RedactPlugin {
    sensitive_keys: vec!["password".to_string(), "api_key".to_string()],
};

let plugins: Vec<Box<dyn Plugin>> = vec![Box::new(plugin)];
let value = parse_with_plugins(json_str, ParserOptions::default(), &plugins)?;

Validation Plugin Example

struct SchemaValidatorPlugin {
    required_fields: Vec<String>,
}

impl Plugin for SchemaValidatorPlugin {
    fn name(&self) -> &str {
        "schema-validator"
    }

    fn transform(&self, _value: &mut Value) -> Result<(), Error> {
        Ok(()) // No transformation needed
    }

    fn validate(&self, value: &Value) -> Result<(), Error> {
        if let Value::Object(map) = value {
            for field in &self.required_fields {
                if !map.contains_key(field) {
                    return Err(Error::Custom(
                        format!("Missing required field: {}", field)
                    ));
                }
            }
        }
        Ok(())
    }
}

NDJSON (Newline-Delimited JSON) Support (New in v2.0.0)

Process streams of JSON objects separated by newlines.

use vexy_json::NdJsonParser;

fn process_log_file(log_content: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut parser = NdJsonParser::new();
    let entries = parser.feed(log_content)?;

    println!("Processed {} log entries", entries.len());

    for (i, entry) in entries.iter().enumerate() {
        if let Some(timestamp) = entry.get("timestamp") {
            println!("Entry {}: {:?}", i, timestamp);
        }
    }

    Ok(())
}

// Example input:
// {"timestamp": "2024-01-01T00:00:00Z", "level": "INFO", "message": "Server started"}
// {"timestamp": "2024-01-01T00:01:00Z", "level": "ERROR", "message": "Connection failed"}
// {"timestamp": "2024-01-01T00:02:00Z", "level": "INFO", "message": "Retry successful"}

Advanced CLI Usage (New in v2.0.0)

The v2.0.0 CLI includes powerful new features:

Watch Mode

# Watch a file for changes and reformat on save
vexy_json --watch config.json --output formatted-config.json

# Watch a directory
vexy_json --watch ./configs/ --output-dir ./formatted/

Batch Processing

# Process multiple files in parallel
vexy_json --parallel *.json --output-dir ./processed/

# Apply transformations during batch processing
vexy_json --batch ./data/ --pretty --sort-keys --output-dir ./formatted/

Plugin Usage

# Use built-in plugins
vexy_json input.json --plugin redact-passwords --plugin validate-schema

# Load custom plugin
vexy_json input.json --plugin-path ./my-plugin.wasm

For more details on the web tool, including its features and how to use it, refer to the Web Tool documentation.