Skip to main content

Crate deser_json

Crate deser_json 

Source
Expand description

Parse and serialize JSON compatible with deser.

This library is very bare bones at this point and not at all optimized. It is based on microserde which in turn is based on miniserde to achieve the most minimal implementation of a serializer and serializer.

let vec: Vec<u64> = deser_json::from_str("[1, 2, 3, 4]").unwrap();
let json = deser_json::to_string(&vec).unwrap();
assert_eq!(json, "[1,2,3,4]");

Besides strings, JSON can also be parsed from byte slices with from_slice. In that case the input is validated as UTF-8 while it is parsed rather than upfront:

let vec: Vec<String> = deser_json::from_slice(b"[\"a\", \"b\"]").unwrap();
assert_eq!(vec, ["a", "b"]);
assert!(deser_json::from_slice::<String>(b"\"\xff\"").is_err());

Integers that do not fit into 64 bits as well as the well-known BigInt, Decimal and Number types are written as JSON numbers. Other well-known types (such as date-times and UUIDs) are written as strings. JSON has no bytes, they are written as base64 strings by default and types that expect bytes accept strings and arrays of integers (see bytes, the format can be configured in the Context with a BytesFormat). Floats are written with the shortest text that reads back as the same value of their precision (0.1f32 as 0.1, not 0.10000000149011612), floats that are infinite or NaN as null.

When parsing, floats whose text cannot be recovered from their value as f64 (like 0.10 or 1e5) and integers that do not fit into 128 bits are passed on as Number which carries the text of the number and its value as f64. Types like f64 get the value, types which deserialize decimal numbers exactly (like Decimal) use the text. See DeserializerConfig::set_exact_numbers.

Strings without escape sequences are borrowed from the input, so types can borrow them:

#[derive(deser::Deserialize)]
struct User<'a> {
    name: &'a str,
}

let user: User = deser_json::from_str(r#"{"name": "Peter"}"#).unwrap();
assert_eq!(user.name, "Peter");

§Raw Values

RawJson holds the JSON text of a value. The JSON of values that are deserialized from JSON is kept as it is: it’s validated but not deserialized, which is faster, and written out again unchanged. With the Borrowed adapter the text is borrowed from the input:

use std::collections::BTreeMap;
use deser::adapters::Borrowed;
use deser_json::RawJson;

#[derive(deser::Deserialize, deser::Serialize)]
struct Envelope<'a> {
    kind: String,
    #[deser(as = Borrowed)]
    payload: RawJson<'a>,
}

let input = r#"{"kind": "point", "payload": {"x": 1.50, "y": 2}}"#;
let envelope: Envelope = deser_json::from_str(input).unwrap();
assert_eq!(envelope.payload.get(), r#"{"x": 1.50, "y": 2}"#);

// deserialized later, once it's known what it is
let point: BTreeMap<String, f64> = envelope.payload.deserialize().unwrap();
assert_eq!(point["x"], 1.5);

// and written out again unchanged
assert_eq!(
    deser_json::to_string(&envelope).unwrap(),
    r#"{"kind":"point","payload":{"x": 1.50, "y": 2}}"#
);

The text is only kept for values in places where the type is known before the value starts: fields of derived structs, standard containers, Option and Box. Everywhere else (like the fields of untagged enums) and for values of other formats (including the other JSON dialects) the value is encoded as JSON. Other formats serialize the value a RawJson holds. See Raw for more information.

§Pretty Printing

By default the output is compact. SerializerConfig::set_pretty indents it and writes spaces after separators:

use deser_json::{Indent, SerializerConfig};

const PRETTY: SerializerConfig =
    SerializerConfig::builder().pretty(Indent::Spaces(2)).build();
let json = PRETTY.to_string(&vec![vec![1, 2]]).unwrap();
assert_eq!(json, "[\n  [\n    1,\n    2\n  ]\n]");

Indentation and spaces can also be configured on their own with SerializerConfig::set_indent and SerializerConfig::set_compact. Maps and sequences with the Layout::Compact hint are written on a single line in indented output, short ones that only contain scalars can be too (see SerializerConfig::set_inline).

§JSON Lines

By default only whitespace may follow a value. What may follow is controlled by DeserializerConfig::set_trailing (see Trailing). With Trailing::Newline a Deserializer reads JSON Lines (also known as NDJSON) one by one:

use deser_json::{Deserializer, DeserializerConfig, Trailing};

let config = DeserializerConfig::builder().trailing(Trailing::Newline).build();
let mut de = Deserializer::from_str_with_config("[1, 2]\n[3]\n", config);
let lines = de.iter::<Vec<u32>>().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(lines, [vec![1, 2], vec![3]]);

Errors only discard their line, so the remaining lines can still be read. Trailing::Stop stops after the value without looking at what follows. JSON Lines are written with a Serializer and SerializerConfig::set_trailing:

use deser_json::{Serializer, SerializerConfig, Trailing};

const LINES: SerializerConfig =
    SerializerConfig::builder().trailing(Trailing::Newline).build();
let mut serializer = Serializer::with_config(LINES);
for value in [vec![1, 2], vec![3]] {
    serializer.serialize(&value).unwrap();
}
assert_eq!(serializer.finish(), "[1,2]\n[3]\n");

§Streams

Values are read from a Read with from_reader and written to a Write with to_writer. To read or write more than one value (for instance JSON Lines from a socket) the configurations create readers and writers of deser::io (DeserializerConfig::reader and SerializerConfig::writer). How values are separated depends on the trailing setting of the configurations (see Trailing). The reader only buffers until a value is complete:

use std::collections::BTreeMap;
use deser_json::{DeserializerConfig, SerializerConfig, Trailing};

const READ_LINES: DeserializerConfig =
    DeserializerConfig::builder().trailing(Trailing::Newline).build();
const WRITE_LINES: SerializerConfig =
    SerializerConfig::builder().trailing(Trailing::Newline).build();

let input = &b"{\"id\": 1}\n{\"id\": 2}\n"[..];
let mut reader = READ_LINES.reader(input);
let mut writer = WRITE_LINES.writer(Vec::new());
while let Some(value) = reader.read::<BTreeMap<String, u32>>().unwrap() {
    writer.write(&value).unwrap();
}
assert_eq!(writer.into_inner(), b"{\"id\":1}\n{\"id\":2}\n");

The stream serializer (Serializer) and the stream deserializer (StreamDeserializer) do not do IO themselves (see deser::stream), they also work with other kinds of IO (for instance async runtimes with deser-tokio) and without the standard library.

§Features

  • io (enabled by default): reading and writing streams of the standard library, see streams. Requires std.
  • speedups (enabled by default): uses simdutf8 to validate UTF-8 when parsing byte slices.
  • std (enabled by default): uses the standard library. Without it this crate only needs alloc (see no_std).

Structs§

Deserializer
Deserializes a serializable from JSON.
DeserializerConfig
Configures how JSON is deserialized.
DeserializerConfigBuilder
Builds a DeserializerConfig.
Iter
An iterator over the values of a JSON stream.
Json
The JSON format of RawJson values.
Serializer
Serializes values into JSON.
SerializerConfig
Configures how values are serialized to JSON.
SerializerConfigBuilder
Builds a SerializerConfig.
StreamDeserializer
Reads a stream of JSON values (see deser::stream).

Enums§

Indent
How the output is indented.
InlinePolicy
When maps and sequences are written on a single line in indented output.
Trailing
Controls what may follow a value.

Functions§

from_reader
Deserializes a value from a reader.
from_slice
Deserializes JSON from the given bytes.
from_str
Deserializes JSON from the given string.
to_string
Serializes a value to JSON.
to_writer
Serializes a value to a writer.

Type Aliases§

RawJson
JSON text of a value.