#json-parser #json-object #serialization #string #order #numbers #field

strason

Json parser that preserves field ordering and stores numbers as strings, preserving their full data

9 releases

Uses old Rust 2015

0.4.0 Jul 28, 2018
0.3.4 May 7, 2016
0.3.3 Nov 22, 2015
0.2.1 Nov 16, 2015
0.1.0 Nov 15, 2015

#1728 in Encoding

Download history 52/week @ 2023-12-08 64/week @ 2023-12-15 64/week @ 2023-12-22 20/week @ 2023-12-29 56/week @ 2024-01-05 82/week @ 2024-01-12 47/week @ 2024-01-19 35/week @ 2024-01-26 32/week @ 2024-02-02 62/week @ 2024-02-09 71/week @ 2024-02-16 84/week @ 2024-02-23 85/week @ 2024-03-01 110/week @ 2024-03-08 106/week @ 2024-03-15 87/week @ 2024-03-22

397 downloads per month
Used in 14 crates (5 directly)

CC0 license

81KB
1.5K SLoC

Status

Strason

Support for stringly-typed Json parsing and serialization.

Documentation

This project differs from other Json parsers in three main ways:

  1. Numbers are read as strings and stored as strings. It is the responsibility of the calling code to parse them. (They are validated to be valid Json numbers.)

  2. The order of fields in Json objects is preserved, for applicatons that care about this. Note that this means objects are stored as a Vec of (key, value) pairs which means accessing fields by name takes linear time. Callers who need to do this may have to unload into their own HashMap.

    It also means that Json objects will not test equal unless the order of their fields matches.

  3. Multiple fields with the same name can be stored and serialized. This shouldn't ever be done, but is useful for interoperating with buggy software.

The library has one type, Json, which is defined as follows:

pub enum Json {
    Null,
    Bool(bool),
    Number(String),
    String(String),
    Array(Vec<Json>),
    Object(Vec<(String, Json)>)
}

That is, except for nulls and booleans, all data are represented by strings. (Actually, the real implementation is hidden, to give me freedom to add, e.g. indexing support for Json::Objects or something without breaking. But this is what it looks like now.)

These objects can be created from raw byte data by the method Json::from_iter, Json::from_str and Json::from_reader. They can be reserialized with the Json::to_bytes() method.

Alternately, Json objects can be created using std::From, such as

let s = "A regular old Rust string".to_owned();
let json = From::from(s);

Implementations are available for all integer types, as well as bool, String and (). To construct a JSON number from a string, use Json::from_str as above so that the number can be validated to have correct form.

Full compliance with ECMA 404 is expected. Any deviations are bugs.

All byte inputs should round-trip (deserialize then serialize) to the same thing, up to (a) variations in whitespace, (b) escaping of the / character in strings, (c) choice of when to use Unicode \uXXXX escapes, and the capitalization of them. Also UCS-2 points that are not UTF-16 (there are few of these and they are meaningless anyway) will be rejected at parse time, unlike most parsers in use. Any deviations beyond this are bugs.

Usage

To use strason, just add the following to your Cargo.toml.

[dependencies]
strason = "0.3"

Serialization and Deserialization

The Json object does not directly support de/serialization through serde. The reason is that the serde API is inherently lossy, not having any support for large numbers. Therefore de/serialization of arbitrary objects as JSON needs to happen in two steps, using Json as an intermediary.

Serialization thus looks something like

let json = strason::from_serialize(my_object).unwrap():
let output = json.to_bytes();

and deserialization like:

let input_json = strason::from_reader(my_data_source);
let output = input_json.into_deserialize();

Dependencies

~110–350KB