MCPcopy Index your code
hub / github.com/callum-oakley/json5-rs

github.com/callum-oakley/json5-rs @1.3.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.3.1 ↗ · + Follow
157 symbols 305 edges 11 files 16 documented · 10%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

JSON5

crates.io docs.rs

JSON5 is a superset of JSON with an expanded syntax including some productions from ECMAScript 5.1. It aims to be easier to write and maintain by hand (e.g. for config files). It is not intended to be used for machine-to-machine communication, for which you'd be better served by serde-rs/json.

In particular, JSON5 allows comments, trailing commas, object keys without quotes, single quoted strings, hexadecimal numbers, multi-line strings...

{
  // comments
  unquoted: "and you can quote me on that",
  singleQuotes: 'I can use "double quotes" here',
  lineBreaks: "Look, Mom! \
No \\n's!",
  hexadecimal: 0xdecaf,
  leadingDecimalPoint: 0.8675309,
  andTrailing: 8675309,
  positiveSign: +1,
  trailingComma: "in objects",
  andIn: ["arrays"],
  backwardsCompatible: "with JSON",
}

This crate provides functions for deserializing JSON5 text into a Rust datatype and for serializing a Rust datatype as JSON5 text, both via the Serde framework.

Deserialization

Implementing serde::Deserialize on your type will allow you to parse JSON5 text into a value of that type with from_str.

use serde_derive::Deserialize;

#[derive(Debug, PartialEq, Deserialize)]
struct Config<'a> {
    foo: u32,
    bar: &'a str,
}

let config: Config = json5::from_str("
  {
    // Note unquoted keys, comments, and trailing commas.
    foo: 42,
    bar: 'baz',
  }
")?;

assert_eq!(config, Config{ foo: 42, bar: "baz" });

There are many ways to customize the deserialization (e.g. deserializing camelCase field names into a struct with snake_case fields). See the Serde docs, especially the Attributes, Custom serialization, and Examples sections.

Serialization

Similarly, implementing serde::Serialize on a Rust type allows you to produce a JSON5 serialization of values of that type with to_string or to_writer. The serializer will omit quotes around object keys where possible and will indent nested objects and arrays, but is otherwise fairly basic.

use serde_derive::Serialize;

#[derive(Serialize)]
struct Config<'a> {
    foo: u32,
    bar: &'a str,
}

let config = Config {
    foo: 42,
    bar: "baz",
};

assert_eq!(&json5::to_string(&config)?, "{
  foo: 42,
  bar: \"baz\",
}");

There are many ways to customize the serialization (e.g. serializing snake_case struct fields as camelCase). See the Serde docs, especially the Attributes, Custom serialization and Examples sections.

Byte arrays

All the types of the Serde data model are supported. Byte arrays are encoded as hex strings. e.g.

use serde_bytes::{Bytes, ByteBuf};

let s = json5::to_string(&Bytes::new(b"JSON5"))?;
assert_eq!(&s, "\"4a534f4e35\"");
assert_eq!(json5::from_str::<ByteBuf>(&s)?, ByteBuf::from("JSON5"));

Benchmarks

There's a criterion benchmark in benches comparing performance against serde-rs/json, spyoungtech/json-five-rs, and google/serde_json5 (which is based on a fork of an earlier version of this library). Throughput on my machine at the time of writing is summarised below. Higher is better.

crate small medium large
serde-rs/json 513.51 MiB/s 519.16 MiB/s 511.84 MiB/s
callum-oakley/json5-rs 227.47 MiB/s 220.99 MiB/s 224.51 MiB/s
spyoungtech/json-five-rs 105.36 MiB/s 107.08 MiB/s 106.45 MiB/s
google/serde_json5 11.500 MiB/s 11.581 MiB/s 10.438 MiB/s

Project goals and non-goals

  • Goal: Strict adherence to the specification. If you find some way the implementation deviates from the spec then please open an issue!
  • Non-goal: I'm not interested in supporting extensions or relaxations of the spec, even if they're gated behind an option. It comes at the cost of code complexity and expands the scope of the project to "anything that looks a bit like JSON5".
  • Goal: "Reasonable" performance given the target use case of deserializing configuration files (not e.g. message passing).
  • Non-goal: Performance in line with serde-rs/json. Lots of work has gone in to making Serde JSON as fast as it is. I'm content to have a simpler codebase and sacrifice some performance (e.g. by working with chars instead of bytes).

Contributing

Please open an issue rather than a pull request.

Core symbols most depended-on inside this repo

next
called by 23
src/de.rs
err_at
called by 22
src/de.rs
skip_whitespace
called by 17
src/de.rs
peek
called by 14
src/de.rs
peek_or
called by 11
src/de.rs
next_or
called by 10
src/de.rs
expect_char
called by 8
src/de.rs
with_position
called by 7
src/de.rs

Shape

Method 83
Function 38
Class 28
Enum 8

Languages

Rust100%

Modules by API surface

src/de.rs57 symbols
src/ser.rs30 symbols
tests/de.rs29 symbols
tests/ser.rs18 symbols
src/error.rs15 symbols
src/char.rs5 symbols
benches/ser.rs1 symbols
benches/json.rs1 symbols
benches/de.rs1 symbols

For agents

$ claude mcp add json5-rs \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page