RUST

JSON to Rust Struct

Generate nested Rust structs with Serde derives, rename attributes, Vec, and Option fields

JSON & Data
πŸ”’ 100% client-side β€” your data never leaves this page
Maintained by Evanβ€’Updated: September 30, 2026
JSON β†’ Rust structs

Inferred from samples, not a complete API contract. Missing fields and explicit null are inferred separately but can share an output type; null array elements remain nullable. Duplicate keys and numbers that would change value are rejected.

Limits: 1 MiB, 64 levels, 20,000 values, 200 models and 2,000 model fields. Use strings for exact large IDs or monetary decimals.

Target: Rust 2021, serde (derive) and serde_json. Mixed values use serde_json::Value, which can require that dependency even without derives.

Rust structs

After generation, compile in your target project and verify deserialization with representative responses.

About this tool

JSON to Rust Struct generates Rust 2021 declarations with optional Serde derives and field renames. It infers missing object fields independently of null values and retains nullability at every array level: an integer-or-null list becomes Vec<Option<i64>>, while a nullable list can be Option<Vec<...>>. Incompatible samples and empty arrays use serde_json::Value where an element type cannot be learned. Disabling Serde derives does not remove this type dependency. Rust keywords receive safe suffixes, including self, super and crate, which cannot simply become raw identifiers. Field names and nested model names are allocated uniquely after normalization, and Serde rename strings preserve original keys and escapes. Missing fields use Option with a default attribute; this model does not distinguish a missing value from explicit null after decoding. The local parser accepts at most 1 MiB, 64 levels, 20,000 values, 200 models and 2,000 fields, rejecting duplicate keys, invalid Unicode and numeric conversions that lose value. Integers are restricted to the browser safe range even though output uses i64. Add serde with derive and serde_json to the target crate, compile, and test actual samples before choosing domain enums or custom deserializers.

Scenario Recipes

01

Check nested nullability in a Rust client

Goal: A tested distinction between a missing value and an integer zero inside a list.

  1. Paste {"self":1,"values":[1,null,2]} and keep Serde enabled.
  2. Generate and confirm self_value has rename = "self" and values uses Vec<Option<i64>>.
  3. Add serde derive and serde_json to a test crate, decode the sample and check that the second element is None.

Result: A tested distinction between a missing value and an integer zero inside a list.

Failure Clinic (Common Pitfalls)

An array fails to decode even though the field is Option

Cause: An optional field does not make each array element nullable.

Fix: Keep the generated inner Option for null elements and test both a null list and a list containing null.

A struct without derives still needs serde_json

Cause: Unknown or mixed values are represented by serde_json::Value.

Fix: Keep the dependency or replace the generic type with a reviewed domain type and decoder.

Production Snippets

Nullable elements and a keyword field

rust

// Input: {"self":1,"values":[1,null,2]}
#[derive(serde::Serialize, serde::Deserialize)]
struct ApiResponse {
    #[serde(rename = "self")]
    self_value: i64,
    values: Vec<Option<i64>>,
}

Frequently Asked Questions

Why is self renamed to self_value?

Rust does not allow self, super or crate as raw identifiers. The generator adds a safe suffix and a Serde rename attribute.

What is the difference between Vec<Option<T>> and Option<Vec<T>>?

The former permits null elements inside a present array. The latter permits the entire array to be null. Both can be needed at the same time.

Can I remove all dependencies by turning Serde off?

Not if mixed or unknown values still use serde_json::Value. The interface shows this requirement; replace those fields only after defining their real contract.

Does Option distinguish missing from null?

No. Both decode to None in this generated model. Track presence separately if the difference matters to partial updates.

Keep browsing