MicroJSON
I published microjson today.
It’s a tiny crate, designed to do exactly one thing — read JSON. It’s not very fast, doesn’t let you serialize objects, doesn’t unpack JSON into custom structs1 and doesn’t expose multiple APIs.
But there is one other thing it doesn’t do which I think is important: it doesn’t allocate any memory.
It also doesn’t have any dependencies.
No allocations: no_std
The use case for microjson
is tiny applications (embedded) that need to extract a small amount of data from some JSON once.
You read in the bytes of the JSON and you run through the data. Random access to arrays is discouraged — instead, iterate through the array2.
This means that we don’t ever need to allocate anything on the heap. And if we don’t need to allocate anything, we shouldn’t allocate anything.
microjson
tries to do this.
We barely allocate anything on the stack, and all “data” is actually pointers to positions in the input string.
At the moment, Rust’s support for switching out/omitting global (or *shudder* non-global) allocators is… sketchy. The easiest way to enforce that we don’t need an allocator is to make the whole crate not depend on the standard library.
It turns out that microjson
doesn’t need (or want) most of what is in the standard library and can survive off the core
library.
For this reason, microjson
is no_std
.
This is great because it opens up the possibility of using the crate in situations where you can’t have a standard library.
Don’t fear though, it can still be used in a std
project!
No dependencies
Rust has a fantastic package manager, crates.io, on which microjson
is published.
It makes including dependencies very easy (just add them to your Cargo.toml
) and encourages code reuse.
In general these are great things, but can lead to dependency creep. Projects depend on crates that depend on crates that depend on crates and so on. Eventually, one simple project could draw in a lot of completely unrelated crates, ballooning compile time, increasing attack surface area, and increasing code complexity3.
microjson
needs no dependencies so you can feel free to include it without worrying that it will bloat your project.
The only dependency in Cargo.toml
is
[dev-dependencies]
criterion = "0.3"
But because this is a dev-dependency
(and only used for benchmarking) it won’t be included in any projects that use microjson
!
Why you should consider microjson
for your next project
Here are some use-cases for when you might like to try this crate out:
- You are doing something embedded and want a small code footprint and no extra memory requirements. Maybe an IoT project that needs to query a REST endpoint and read a small amount of JSON from the response.
- You don’t want to include another big dependency and just need to read a small (or large — it’s not fast, but it’s also not too slow) amount of JSON.
Some Sample Code
Here’s some brief sample code from the docs.
let input = r#" [0, 1, 2, 3, 4, 5] "#;
let array = JSONValue::parse(input)?;
for (n, item) in array.iter_array()?.enumerate() {
let value = item.read_integer();
assert_eq!(value, Ok(n as isize));
}
Note how we iterate through the array, rather than accessing into it. Objects are similar to use:
let input = r#" { "arr": [3, "foo", 3.625, false] } "#;
let object = JSONValue::parse(input)?;
assert_eq!(
object.get_key_value("arr")?
.iter_array()?
.nth(2).unwrap()
.read_float(),
Ok(3.625)
);
Why you should consider something else for your next project
On the other hand, there are plenty of other options out there.
Perhaps you know the format of your incoming JSON exactly and want it de-serialised straight to a struct. Perhaps you need to generate JSON. Or perhaps you want blinding speed and a full suite of tests that will make your eyes water.
Here is (a very non-exhaustive) list of options:
tinyjson
: generates and reads JSON, has no dependencies, but stores its values inVec
s andHashMap
s. Looks like a really clean API though.serde_json
: the serde crate for JSON. Includes kitchen sink.miniserde
: seems to have a few similar design goals tomicrojson
but is struct based.
Its also a personal project and there is a lot of scope for improvement. If you need production-ready, bulletproof libraries, you may want to look elsewhere.
I’d appreciate feedback, both on microjson
itself and if I’ve maligned/omitted any other JSON parsing crates. I haven’t had time to try them all out, as none seemed to tick all the boxes I needed for my project exactly.
Please let me know if you have any comments, and as always, feel free to create issues and pull requests on GitHub.
-
On the list of TODOs is to implement iterating through key/value pairs on objects. ↩︎
-
One example of this was an issue with minifb, a window creation library that among its build dependencies needed clap, an incredibly powerful command line parser. The library itself never touched the command line, but in some compiles,
clap
took the majority of the compilation time. One feels sure that a more minimalistic parser could have been included with the offending dependency, the ubiquitousbindgen
. ↩︎