Skip to content

JSON to Rust Struct Converter

Infer reviewable Serde Rust structs from JSON, including nested objects, vectors, optional fields, nulls, renames, and normalized identifier collisions.

Privacy: your files never leave your device. All processing happens locally in your browser.

How to use

  1. 1.Paste representative strict JSON and enter an ASCII root type name.
  2. 2.Generate and review numeric widths, Options, mixed values, nested names, and serde renames.
  3. 3.Copy into a Serde-enabled Rust project, format it, compile it, and round-trip real fixtures.

About JSON to Rust Struct Converter

JSON to Rust Struct Converter turns one strict JSON sample into Rust source using Serde derives. Paste an object, array, or primitive, choose a root type name, generate, inspect, and copy. The page emits source text only. It does not compile Rust, install crates, execute generated code, or upload the sample.

Strings map to owned String, Booleans to bool, safe JSON integers to i64, and other JSON numbers to f64. Arrays become Vec of the merged element type. Empty arrays use Vec<serde_json::Value> because no sample evidence establishes an element type. Null becomes Option<serde_json::Value>, while null combined with an observed type becomes Option of that type.

Objects become public structs with Serialize, Deserialize, and Debug derives. Nested objects become named nested structs. When an array contains objects, fields observed in every object remain required and fields missing from at least one object become Option. Integer and decimal observations merge to f64. Incompatible mixed values fall back to serde_json::Value rather than inventing an enum or silently choosing the first type.

JSON keys are converted to ASCII snake_case Rust fields. Camel case boundaries, spaces, punctuation, and hyphens become underscores. Leading digits are prefixed, Rust keywords receive a trailing underscore, and normalized collisions receive numeric suffixes. Whenever the Rust field differs from the JSON key, a serde rename attribute preserves the original wire name. Nested type names are derived safely from their path.

Inference from one sample cannot establish an API contract. A field present here may be optional elsewhere; a small integer may require u64, i128, Decimal, or a string; a string may represent a UUID, date, URL, enum, borrowed slice, or secret; and mixed objects may deserve a tagged enum. Review authoritative API documentation and several success and failure fixtures.

The generated import assumes serde derive support. JSON parsing in a destination also normally requires serde_json. This site introduces neither dependency; the copied code belongs in a project whose Cargo.toml pins compatible versions. The generator does not add crate paths, module visibility policy, lifetimes, generics, custom serializers, deny_unknown_fields, defaults, flattening, tagging, or validation.

Strict JSON is required. Comments, trailing commas, NaN, Infinity, BigInt syntax, undefined, and JavaScript object literals are rejected. JSON numbers are parsed by JavaScript before inference, so unsafe integer spellings cannot be represented exactly and fall back away from i64 inference. Keep precision-sensitive identifiers and decimals as strings in the source contract.

Work is bounded at 500,000 input characters, 50,000 values, 40 nesting levels, and 1,000,000 output characters. Eight external fixtures lock String, i64, f64, bool, Vec, Option, null, and keyword rename syntax. Additional tests cover nested structs, normalized collisions, and invalid input. Format and compile the copied source, then round-trip representative JSON with serde_json before adopting it.

Methodology & sources

Parses strict JSON, recursively infers Rust primitives and containers, merges object samples with missing fields as Option, falls back to serde_json::Value for incompatible evidence, normalizes collision-safe Rust identifiers, adds serde rename attributes when wire keys differ, and emits Serialize/Deserialize derives.

Frequently asked questions

Why did a mixed field become serde_json::Value?
One sample does not justify a safe enum or coercion rule. Value preserves uncertainty until you define the real contract.
Why is a missing field Option?
When merged object samples do not all contain a key, Option represents that observed absence. Broader production data may require more changes.
Does the output compile without dependencies?
The destination project needs compatible serde derive support and usually serde_json. This page only generates source text.

Developer Tools guides

View all