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;">&lt;</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">&gt;,</span>

}

Success

Person {
  name"Alice",
  age30,
  emailOption::Some("alice@example.com"),
}

JSON Output

{
  "name": "Alice",
  "age": 30,
  "email": "alice@example.com"
}

Nested Structs

Struct containing nested struct and vector.

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;">&lt;</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">&gt;,</span>

}

#[derive(Facet)] struct Address { street: String,

<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",
  addressAddress {
    street"123 Main St",
    city"Springfield",
  },
  employeesVec<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",
    width800,
  },
  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 {
    code404,
    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 {
    x100,
    y200,
  },
  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;">&lt;</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">&gt;,</span>

}

Success

Config {
  debugtrue,
  max_connections100,
  endpointsVec<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;">&lt;</span><span style="color:#2ac3de;">String</span><span style="color:#a9b1d6;">&gt;,</span>

}

Success

Config {
  debugtrue,
  max_connections100,
  endpointsVec<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

@invalid
Target 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 stuff
Target 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    ╰────