#json-parser #json #borrowing #parse-json #data #document

no-std justjson

An efficient JSON Value crate that allows borrowing data

6 releases

0.3.0 Dec 8, 2023
0.2.3 Apr 11, 2023
0.2.2 Feb 2, 2023
0.2.1 Jan 25, 2023
0.1.1 Jan 20, 2023

#253 in Parser implementations

Download history 55/week @ 2023-12-11 212/week @ 2023-12-18 106/week @ 2023-12-25 212/week @ 2024-01-01 140/week @ 2024-01-08 26/week @ 2024-01-15 8/week @ 2024-01-22 2/week @ 2024-01-29 23/week @ 2024-02-05 13/week @ 2024-02-12 48/week @ 2024-02-19 55/week @ 2024-02-26 49/week @ 2024-03-04 36/week @ 2024-03-11 48/week @ 2024-03-18 22/week @ 2024-03-25

161 downloads per month
Used in 3 crates (via kludgine)

MIT/Apache

190KB
4.5K SLoC

justjson

crate version Live Build Status HTML Coverage Report for main branch Documentation

An efficient JSON Value crate for Rust.

This crate is thoroughly tested, but it's still very early in development. Feedback is greatly appreciated.

Why another JSON library?

This library is for developers who:

  • Are working with JSON, but either don't want to or can't use serde.
  • Want to parse a JSON Value from a slice with minimal allocations.

If neither of those situations apply to you, you should seriously consider using serde and serde-json or simd-json.

What real-world case does this use case fit? Parsing JSON-LD Compacted Document Form requires inspecting the JSON to find the @context field, and using the information within the @context field to interpret the rest of the document. This form of JSON-LD representation is used by ActivityPub, which is a protocol that power the Fediverse.

What makes JustJson interesting?

The optimal use case for JustJson is when parsing an &str or &[u8]. When parsing either of these, the returned Value type is Value<&str>. All strings and numbers are kept in their original form in the JsonString and JsonNumber types. While they are kept in their original form, they are fully validated when parsed. The net effect is significantly fewer allocations when parsing a JSON value.

JsonString implements PartialEq<&str>/PartialCmp<&str> such that if the JsonString contains escape sequences, the comparison is handled correctly. Each JsonString tracks its decoded length as well as whether any escapes are present, allowing this comparison to be very efficient.

let json = justjson::JsonString::from_json("\"Hello, World!\"").unwrap();
assert_eq!(json, "Hello, World!");

JustJson also offers an even faster method for parsing: Document. When parsing an array or an object into a Value, the parser doesn't know how large each array or object will be until it parses the rest of the object. Document builds a tree representing the JSON value in a single Vec, further reducing the number of allocations needed when parsing a document.

This extra speed comes at the expense of an API that requires iteration to inspect the Document.

Benchmarks

You can run the benchmarks by executing RUSTFLAGS="-C target-cpu=native" cargo bench -p benchmarks --all-features. There currently is only a single benchmark, testing a small JSON payload in both pretty and compact representations.

small-pretty/justjson/str
                        time:   [802.34 ns 804.91 ns 807.35 ns]
small-pretty/justjson/doc/str
                        time:   [603.15 ns 607.13 ns 611.21 ns]
small-pretty/justjson/bytes
                        time:   [774.45 ns 775.84 ns 777.37 ns]
small-pretty/justjson/doc/bytes
                        time:   [688.51 ns 696.55 ns 705.09 ns]
small-pretty/serde-json/str
                        time:   [860.82 ns 862.12 ns 863.43 ns]
small-pretty/serde-json/bytes
                        time:   [917.49 ns 920.17 ns 923.26 ns]
small-pretty/simd-json/bytes
                        time:   [702.67 ns 704.71 ns 706.61 ns]
small-pretty/json-deserializer/bytes
                        time:   [858.81 ns 861.37 ns 864.88 ns]

small/justjson/str      time:   [736.80 ns 739.94 ns 743.56 ns]
small/justjson/doc/str  time:   [611.54 ns 618.64 ns 625.15 ns]
small/justjson/bytes    time:   [710.26 ns 711.53 ns 712.86 ns]
small/justjson/doc/bytes
                        time:   [588.97 ns 594.66 ns 600.98 ns]
small/serde-json/str    time:   [831.89 ns 833.17 ns 834.69 ns]
small/serde-json/bytes  time:   [874.84 ns 876.26 ns 877.90 ns]
small/simd-json/bytes   time:   [690.41 ns 691.48 ns 692.79 ns]
small/json-deserializer/bytes
                        time:   [778.87 ns 779.85 ns 780.85 ns]

Usage of Unsafe Code

This crate uses unsafe code only when converting from raw incoming data to UTF-8 data. The parser fully verifies that the data is valid UTF-8 before these functions are used.

no_std support

By default, this crate enables the std feature, which adds support for Rust's standard library types. By disabling default features, this crate can be used in no_std projects. For example, in your Cargo.toml:

[dependencies]
justjson = { version = "*", default-features = false }

The Value type requires the alloc feature to be enabled.

The Document type alias requires the alloc feature, but the GenericDocument type allows providing your own collection type.

With the heapless feature enabled, the HeaplessDocument type can be used to parse documents using the heapless crate's Vec type for storage. This enables parsing JSON in environments where the alloc crate isn't supported.

Open-source Licenses

This project, like all projects from Khonsu Labs, is open-source. This repository is available under the MIT License or the Apache License 2.0.

To learn more about contributing, please see CONTRIBUTING.md.

Dependencies