JSON
facet-json provides JSON serialization and deserialization for any type that implements Facet. It includes rich error diagnostics with source locations and typo suggestions.
Basic Struct
Simple struct with optional field serialized to JSON. }Target Type
#[derive(Facet)]
struct Person {
name: String,
<span style="color:#7aa2f7;">age</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">u32</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">email</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">Option</span><span style="color:#a9b1d6;"><</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">>,</span>
Success
Person {
name: "Alice",
age: 30,
email: Option::Some("alice@example.com"),
}JSON Output
{
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}Nested Structs
Struct containing nested struct and vector. } #[derive(Facet)]
struct Address {
street: String, }Target Type
#[derive(Facet)]
struct Company {
name: String,
<span style="color:#7aa2f7;">address</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">Address</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">employees</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">Vec</span><span style="color:#a9b1d6;"><</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">>,</span>
<span style="color:#7aa2f7;">city</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">,</span>
Success
Company {
name: "Acme Corp",
address: Address {
street: "123 Main St",
city: "Springfield",
},
employees: Vec<String> ["Bob", "Carol", "Dave"],
}JSON Output
{
"name": "Acme Corp",
"address": {
"street": "123 Main St",
"city": "Springfield"
},
"employees": [
"Bob",
"Carol",
"Dave"
]
}Externally Tagged Enum (default)
Default enum serialization with external tagging: {"Variant": content}Target Type
Success
[Message; 3] [
Message::Text("Hello, world!"),
Message::Image {
url: "https://example.com/cat.jpg",
width: 800,
},
Message::Ping,
]JSON Output
[
{
"Text": "Hello, world!"
},
{
"Image": {
"url": "https://example.com/cat.jpg",
"width": 800
}
},
"Ping"
]Internally Tagged Enum
Enum with internal tagging using #[facet(tag = "type")] - variant name becomes a field.Target Type
Success
[ApiResponse; 2] [
ApiResponse::Success {
data: "Operation completed",
},
ApiResponse::Error {
code: 404,
message: "Not found",
},
]JSON Output
[
{
"type": "Success",
"data": "Operation completed"
},
{
"type": "Error",
"code": 404,
"message": "Not found"
}
]Adjacently Tagged Enum
Enum with adjacent tagging using #[facet(tag = "t", content = "c")] - variant name and content are separate fields.Target Type
Success
[Event; 3] [
Event::Click {
x: 100,
y: 200,
},
Event::KeyPress(A),
Event::Resize,
]JSON Output
[
{
"t": "Click",
"c": {
"x": 100,
"y": 200
}
},
{
"t": "KeyPress",
"c": "A"
},
{
"t": "Resize"
}
]Untagged Enum
Enum with #[facet(untagged)] - no tagging, relies on JSON structure to determine variant.Target Type
Success
[StringOrNumber; 2] [StringOrNumber::Str("hello"), StringOrNumber::Num(42)]JSON Output
[
"hello",
42
]Maps with String Keys
HashMap with string keys serializes to JSON object.Target Type
Success
HashMap<String, i32> [
"one" => 1,
"two" => 2,
]JSON Output
{
"one": 1,
"two": 2
}Maps with Integer Keys
HashMap with integer keys - keys are stringified for JSON compatibility.Target Type
Success
HashMap<i32, String> [
1 => "one",
2 => "two",
]JSON Output
{
"1": "one",
"2": "two"
}Tuple Struct
Tuple struct serializes as JSON array.Target Type
#[derive(Facet)]
struct Point(i32, i32, i32);
Success
Point(
10,
20,
30,
)JSON Output
[
10,
20,
30
]Compact JSON Output
Compact serialization - all on one line, minimal whitespace. }Target Type
#[derive(Facet)]
struct Config {
debug: bool,
<span style="color:#7aa2f7;">max_connections</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">u32</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">endpoints</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">Vec</span><span style="color:#a9b1d6;"><</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">>,</span>
Success
Config {
debug: true,
max_connections: 100,
endpoints: Vec<String> ["https://api1.example.com", "https://api2.example.com"],
}JSON Output
{"debug":true,"max_connections":100,"endpoints":["https://api1.example.com","https://api2.example.com"]}Pretty JSON Output
Pretty-printed serialization - formatted with indentation and newlines. }Target Type
#[derive(Facet)]
struct Config {
debug: bool,
<span style="color:#7aa2f7;">max_connections</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">u32</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">endpoints</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">Vec</span><span style="color:#a9b1d6;"><</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">>,</span>
Success
Config {
debug: true,
max_connections: 100,
endpoints: Vec<String> ["https://api1.example.com", "https://api2.example.com"],
}JSON Output
{
"debug": true,
"max_connections": 100,
"endpoints": [
"https://api1.example.com",
"https://api2.example.com"
]
}Syntax Error: Unexpected Character
Invalid character at the start of JSON input.
JSON Input
@invalidTarget Type
Error
json::scan
× UnexpectedChar('@') (while parsing i32)
╭────
1 │ @invalid
· ┬
· ╰── unexpected '@', expected i32
╰────
Syntax Error: Invalid Character in Object
Invalid character appears mid-parse with surrounding context visible.
JSON Input
{"name": "test", "value": @bad}Target Type
#[derive(Facet)]
struct Data {
name: String,
<span style="color:#7aa2f7;">value</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">i32</span><span style="color:#a9b1d6;">,</span>
}
Error
json::scan
× UnexpectedChar('@') (while parsing i32)
╭────
1 │ {"name": "test", "value": @bad}
· ┬
· ╰── unexpected '@', expected i32
╰────
Syntax Error: Multiline JSON
Error location is correctly identified in multiline JSON.
JSON Input
{
"name": "test",
"count": ???,
"active": true
}Target Type
#[derive(Facet)]
struct Config {
name: String,
<span style="color:#7aa2f7;">count</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">i32</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">active</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">bool</span><span style="color:#a9b1d6;">,</span>
}
Error
json::scan
× UnexpectedChar('?') (while parsing i32)
╭─[3:12]
2 │ "name": "test",
3 │ "count": ???,
· ┬
· ╰── unexpected '?', expected i32
4 │ "active": true
╰────
Unknown Field
JSON contains a field that doesn't exist in the target struct.
The error shows the unknown field and lists valid alternatives.
JSON Input
{"username": "alice", "emial": "alice@example.com"}Target Type
#[derive(Facet)]
#[facet(deny_unknown_fields)]
struct User {
username: String,
<span style="color:#7aa2f7;">email</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">,</span>
}
Error
json::unknown_field
× unknown field `emial`, expected one of: ["username", "email"] (did you mean `email`?)
╭────
1 │ {"username": "alice", "emial": "alice@example.com"}
· ───┬───
· ╰── unknown field 'emial' - did you mean 'email'?
╰────
Type Mismatch
JSON value type doesn't match the expected Rust type.
JSON Input
{"id": 42, "name": 123}Target Type
#[derive(Facet)]
struct Item {
id: u64,
<span style="color:#7aa2f7;">name</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">,</span>
}
Error
json::type_mismatch
× type mismatch: expected String, got unsigned integer
╭────
1 │ {"id": 42, "name": 123}
· ─┬─
· ╰── expected String, got unsigned integer
╰────
Missing Required Field
JSON is missing a required field that has no default.
JSON Input
{"host": "localhost"}Target Type
#[derive(Facet)]
struct ServerConfig {
host: String,
<span style="color:#7aa2f7;">port</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">u16</span><span style="color:#a9b1d6;">,</span>
}
Error
json::missing_field
× missing required field `port`
╭────
1 │ {"host": "localhost"}
· ┬ ┬
· │ ╰── object ended without field `port`
· ╰── object started here
╰────
Number Out of Range
JSON number is too large for the target integer type.
JSON Input
{"count": 999999999999}Target Type
#[derive(Facet)]
struct Counter {
count: u32,
}Error
json::number_out_of_range
× number `999999999999` out of range for u32
╭────
1 │ {"count": 999999999999}
· ──────┬─────
· ╰── out of range for u32
╰────
Expected Array, Got String
JSON has a string where an array was expected.
JSON Input
{"items": "not an array"}Target Type
#[derive(Facet)]
struct Container {
items: Vec<i32>,
}Error
json::unexpected_token
× unexpected token: got String("not an array"), expected '['
╭────
1 │ {"items": "not an array"}
· ───────┬──────
· ╰── expected '[', got 'String("not an array")'
╰────
Tuple Size Mismatch
JSON array has wrong number of elements for tuple type.
JSON Input
[1, 2, 3]Target Type
#[derive(Facet)]
struct (…)(i32, i32);Error
json::unexpected_token
× unexpected token: got Comma, expected ']'
╭────
1 │ [1, 2, 3]
· ┬
· ╰── expected ']', got 'Comma'
╰────
Unknown Enum Variant
JSON specifies a variant name that doesn't exist.
JSON Input
"Unknown"Target Type
#[derive(Facet)]
#[repr(u8)]
enum Status {
Active,
Inactive<span style="color:#a9b1d6;">,</span>
Pending<span style="color:#a9b1d6;">,</span>
}
Error
json::reflect
× reflection error: Operation failed on shape Status: No variant found with the given name
Wrong Variant Format
Externally tagged enum expects {"Variant": content} but got wrong format.
JSON Input
{"type": "Text", "content": "hello"}Target Type
#[derive(Facet)]
#[repr(u8)]
enum MessageError {
Text(String),
Number<span style="color:#a9b1d6;">(</span><span style="color:#2ac3de;">i32</span><span style="color:#a9b1d6;">),</span>
}
Error
json::reflect
× reflection error: Operation failed on shape MessageError: No variant found with the given name
Internally Tagged Enum: Missing Tag Field
Internally tagged enum requires the tag field to be present.
JSON Input
{"id": "123", "method": "ping"}Target Type
#[derive(Facet)]
#[repr(u32)]
#[facet(tag = "type")]
enum Request {
Ping {
id: String,
},
Echo <span style="color:#a9b1d6;">{</span>
<span style="color:#7aa2f7;">id</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">,</span>
<span style="color:#7aa2f7;">message</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">,</span>
<span style="color:#a9b1d6;">},</span>
}
Error
json::reflect
× reflection error: Operation failed on shape Request: No variant found with the given name
Trailing Data After Valid JSON
Valid JSON followed by unexpected extra content.
JSON Input
42 extra stuffTarget Type
Error
json::scan
× UnexpectedChar('e')
Empty Input
No JSON content at all.
JSON Input
Target Type
Error
json::unexpected_token
× unexpected token: got Eof, expected scalar value
╭────
╰────
Error with Unicode Content
Error reporting handles unicode correctly.
JSON Input
{"emoji": "🎉🚀", "count": nope}Target Type
#[derive(Facet)]
struct EmojiData {
emoji: String,
<span style="color:#7aa2f7;">count</span><span style="color:#a9b1d6;">:</span> <span style="color:#2ac3de;">i32</span><span style="color:#a9b1d6;">,</span>
}
Error
json::scan
× UnexpectedChar('o') (while parsing i32)
╭────
1 │ {"emoji": "🎉🚀", "count": nope}
· ┬
· ╰── unexpected 'o', expected i32
╰────