Skip to main content

Crate jsonx

Crate jsonx 

Source
Expand description

A serde-enabled implementation of the JSONX format.

JSONX is a superset of JSON that relaxes the syntax and adds a handful of typed value constructors. Concretely, on top of JSON it supports:

  • Unquoted object keys matching ^[A-Za-z_][0-9A-Za-z_]*$.
  • Trailing commas after the last array/object element.
  • Typed constructors written as type(value):
    • sized integers: int8, int16, int32, int64, uint8, uint16, uint32, uint64, and the machine-width int / uint;
    • datetime("2017-12-25T15:00:00Z") (RFC 3339);
    • ip("192.168.1.2") / ip("::1");
    • ipport("192.168.1.2:65000") / ipport("[::1]:65000");
    • bytes("YWJjZA==") (standard Base64).

Plain JSON numbers always decode to an f64, exactly as in the reference implementation; the constructors above are how you get a typed integer or one of the extended types.

§Quick start

Work with arbitrary documents via Value:

let value: jsonx::Value = jsonx::from_str("{ id: int(7), tags: [\"a\", \"b\",] }").unwrap();
assert_eq!(value.get("id"), Some(&jsonx::Value::Int(7)));

let text = jsonx::to_string(&value).unwrap();
assert_eq!(text, r#"{id:int(7),tags:["a","b"]}"#);

Or derive Serialize/Deserialize on your own types. Rust integer types map to the matching JSONX sized integers, and plain f64s stay bare:

#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
struct Server {
    name: String,
    port: u16,
    weight: i32,
}

let server = Server { name: "db".into(), port: 5432, weight: -1 };
let text = jsonx::to_string(&server).unwrap();
assert_eq!(text, r#"{name:"db",port:uint16(5432),weight:int32(-1)}"#);
assert_eq!(jsonx::from_str::<Server>(&text).unwrap(), server);

§Extended types

For the non-integer extended types, the most ergonomic option is the wrapper types in this crate — Bytes, Int, Uint, Ip, IpPort, and Datetime. They need no attribute, and because they stay transparent in other serde formats, the same struct also serializes cleanly to (say) TOML or JSON, where they degrade to plain strings/integers:

use jsonx::{Ip, Datetime, DateTime};

#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
struct Peer {
    addr: Ip,
    seen: Datetime,
}

let peer = Peer {
    addr: Ip("10.0.0.1".parse().unwrap()),
    seen: Datetime(DateTime::parse_from_rfc3339("2024-06-01T09:00:00Z").unwrap()),
};
let text = jsonx::to_string(&peer).unwrap();
assert_eq!(text, r#"{addr:ip("10.0.0.1"),seen:datetime("2024-06-01T09:00:00Z")}"#);

If you must keep a bare std::net address or DateTime field, the ip, ipport, and datetime modules provide #[serde(with = ...)] glue instead.

§Extending JSONX with your own constructors

JSONX is open: you can teach it a type(value) constructor for one of your own types by implementing JsonxConstructor. See the constructor module for the details.

§Non-greedy decoding

from_str_partial decodes a single value and tells you where it stopped, so you can parse a stream of concatenated values.

Re-exports§

pub use constructor::deserialize_constructor;
pub use constructor::serialize_constructor;
pub use constructor::JsonxConstructor;
pub use datetime::DateTime;
pub use error::Error;
pub use error::Result;

Modules§

constructor
Open extension API: render your own types as JSONX name(value) constructors.
datetime
RFC 3339 date-time support for the datetime(...) type, backed by chrono.
error
Error and result types for jsonx.
ip
(De)serializes an IpAddr field as JSONX ip("...").
ipport
(De)serializes a SocketAddr field as JSONX ipport("...").

Macros§

ctor
Encodes a constructor name into the &'static str token the jsonx serializer recognizes. Use it to set JsonxConstructor::TOKEN.

Structs§

Bytes
Wraps a byte buffer so it (de)serializes as bytes("...") (Base64).
Datetime
Wraps a DateTime so it (de)serializes as JSONX datetime("...") with no attribute, and as a plain RFC 3339 string in other formats.
Deserializer
A JSONX deserializer over a byte slice. Strings borrow from the input when they contain no escapes.
Int
Wraps an i64 so it serializes as int(...) (the machine-width signed integer). It deserializes from any JSONX integer form.
Ip
Wraps an IpAddr so it (de)serializes as JSONX ip("...") with no #[serde(with = ...)] attribute. Other serde formats see a transparent newtype, so it stays a plain string there (e.g. "10.0.0.1" in TOML/JSON).
IpPort
Wraps a SocketAddr so it (de)serializes as JSONX ipport("...") with no attribute, and as a plain string in other formats. The IpPort analogue of Ip; see ipport for the bare-SocketAddr alternative.
Serializer
A JSONX serializer writing to an io::Write.
Uint
Wraps a u64 so it serializes as uint(...) (the machine-width unsigned integer). It deserializes from any non-negative JSONX integer form.

Enums§

Value
A dynamically-typed JSONX value.

Functions§

from_slice
Deserializes a T from JSONX bytes, requiring that the entire input is consumed (only trailing whitespace is allowed).
from_str
Deserializes a T from a JSONX string, requiring that the entire input is consumed (only trailing whitespace is allowed).
from_str_partial
Non-greedy decoding: deserializes a single top-level value and returns it together with the byte offset of the first byte that was not consumed (after skipping trailing whitespace). The offset equals the input length when nothing follows the value.
to_string
Serializes a value to a compact JSONX String.
to_string_indent
Serializes a value to pretty-printed JSONX with a custom line prefix and per-level indent.
to_string_pretty
Serializes a value to pretty-printed JSONX using a two-space indent.
to_vec
Serializes a value to a compact JSONX byte vector.
to_writer
Serializes a value as compact JSONX to an io::Write.
to_writer_pretty
Serializes a value as pretty JSONX (two-space indent) to an io::Write.

Type Aliases§

Map
A JSONX object.

Derive Macros§

JsonxConstructor
Derives JsonxConstructor plus the serde impls that wire it in, for a type that implements Display and FromStr.