CalcCafe

JSON to Crystal

Paste JSON and get ready-to-use Crystal structs with inferred types and JSON::Serializable.

Reviewed by the CalcCafe editorial team · Last updated 1 July 2026 · How we test our tools

Example

Given this JSON:

{ "id": 1, "name": "Ada", "tags": ["x", "y"], "address": { "city": "NYC" } }

You get:

require "json"

struct Root
 include JSON::Serializable

 property id : Int64
 property name : String
 property tags : Array(String)
 property address : Address
end

struct Address
 include JSON::Serializable

 property city : String
end

How it works

It parses your JSON, infers a Crystal type for each value (String, Int64, Float64, Bool, arrays, nested structs), and emits struct definitions that include JSON::Serializable. The root object becomes a struct named Root.

Good to know

JSON to Crystal turns a sample JSON payload into ready-to-paste Crystal struct definitions that use the standard JSON::Serializable mixin. It is aimed at Crystal developers who are wiring up an API client, parsing a config file, or modeling a webhook body and want strongly typed structs without writing every property declaration by hand.

Reach for it whenever you have a concrete example of the data but no schema. Paste one representative response, click Convert, and you get a struct graph where the top-level object is named Root, nested objects become their own structs named after their parent key, and arrays of consistent objects share a single element type. Because everything runs in your browser, you can safely paste a real API response containing tokens or IDs while you sketch out your types.

Read the output top to bottom: each property name : Type line tells you what Crystal will expect when deserializing. Watch for two signals in particular. A field typed JSON::Any or Array(JSON::Any) means the tool could not commit to one type (an empty array, a null, or mixed element types), and any @[JSON::Field(key: "...")] annotation marks a key that was renamed to a legal Crystal identifier while preserving the original JSON key.

Treat the result as a strong first draft rather than a final schema. Type inference only sees the single example you paste, so a number that happens to be whole in your sample becomes Int64 even if the API can return decimals, and a field that was present becomes non-nullable even if it is sometimes omitted. Review optional and nullable fields by hand, mark them with ? where the API allows missing or null values, and consider feeding a payload that exercises the edge cases you care about.

Frequently asked questions

How are object keys that are not valid Crystal identifiers handled?
Keys like "external-id" cannot be method names in Crystal, so the tool generates a sanitized property name (external_id) and adds a @[JSON::Field(key: "external-id")] annotation so JSON parsing still maps to the original key.
What types does it infer for numbers and mixed arrays?
Integer JSON numbers become Int64 and decimals become Float64. If an array's elements all share one inferred type you get Array(that type); if they differ, it falls back to Array(JSON::Any) to stay safe.
Is my data uploaded anywhere?
No — it runs entirely in your browser. Your input never leaves your device and it works offline once loaded.
Is it free?
Yes, completely free with no sign-up and no limits.

People also ask

What is Crystal's JSON::Serializable?
JSON::Serializable is a module in Crystal's standard library that you include in a struct or class to automatically generate JSON parsing and serialization based on the type's declared properties. Once included, you can call MyType.from_json(string) to deserialize and to_json on an instance to serialize.
How do I parse JSON in Crystal using the generated struct?
After pasting the generated code into your project, call the root type's from_json method with your JSON string, for example Root.from_json(json_string). Crystal reads each property's declared type and maps the matching JSON keys onto it.
Should I use struct or class for JSON in Crystal?
This tool emits struct, which is a value type and is well suited to small, immutable data records. If you need reference semantics, inheritance, or to mutate shared instances, you can change the keyword to class; JSON::Serializable works with both.
How do I make a field optional or nullable in the generated Crystal code?
Add a question mark to the type, such as property name : String?, to allow a value to be nil. In Crystal a nilable property is also treated as optional during deserialization, so a missing key will not raise an error.
Why is a field typed as JSON::Any in the output?
JSON::Any is Crystal's catch-all type for values whose shape could not be pinned down, such as an empty array, a null value, or an array whose elements have differing types. You can replace it with a more specific type once you know the real structure.
Does Int64 versus Int32 matter for the generated structs?
The tool defaults all integers to Int64, which safely holds large values like timestamps and IDs. If you know a field always fits in a smaller range and want to save memory, you can change it to Int32, but Int64 is the conservative choice.
How do I install the Crystal compiler to use these structs?
Crystal is available through package managers such as Homebrew (brew install crystal) on macOS, apt or the official repositories on Linux, and there is an online playground for quick tests. After installing, you compile or run files with the crystal command.
How do I convert a JSON file?
Open the .json file in any text editor, copy its contents, and paste them into the converter for the format you want; this page turns it into Crystal structs, and the same approach works for any other target. Converting a JSON file just means reading its text, which is already plain UTF-8, and producing another representation: a typed model like the Crystal output here, another data format such as YAML or CSV, or a validation contract such as a JSON Schema. There is no single convert action, because the destination decides what conversion means.
Is XML replaced by JSON?
JSON has replaced XML as the default format for web and mobile APIs, largely because it maps directly onto the objects, arrays, strings, and numbers of most programming languages and is shorter to write. XML has not gone away: SOAP services, RSS and Atom feeds, SVG, Office document formats, Android and Maven configuration files, and many enterprise integrations still depend on it. A Crystal developer is far more likely to meet JSON, which is why the JSON::Serializable mixin is part of the standard library.
How can I generate a schema from JSON?
Paste a representative JSON sample into a JSON to JSON Schema generator (this site has one) and it infers a draft-07 schema by reading each value's type: strings, integers, numbers, booleans, nulls, arrays with an items schema, and objects with properties and a required list. The result is a starting point rather than a contract, because a single sample cannot tell the tool which fields are optional or what value ranges are allowed. Edit the required list and add constraints such as enum, minimum, or pattern before using it for validation.
How can I convert JSON to a string?
In Crystal call to_json on any value: a struct generated here that includes JSON::Serializable gets a to_json method, so root.to_json returns the compact JSON string and root.to_pretty_json returns an indented one. Going the other way, Root.from_json(str) parses a string into the struct. In JavaScript the equivalent is JSON.stringify(obj), and every major language has a similar serialize call.
How can I convert JSON data to text?
JSON already is text, so converting it to text usually means one of two things. If you hold a parsed object in memory, serialize it back to a string (in Crystal, value.to_json or value.to_pretty_json). If you want it human-readable, run the JSON through a formatter that adds line breaks and indentation, which changes only whitespace and nothing about the data.
How can I convert JSON to TypeScript?
Paste the JSON into a JSON to TypeScript generator (this site has one alongside this Crystal converter) and it emits an interface per object shape. For the example on this page you would get interface Root { id: number; name: string; tags: string[]; address: Address; } and interface Address { city: string; }. The inference works the same way as the Crystal output: nested objects become their own interface and arrays take the type of their elements.

Related calculators

Sources & references

These tools follow our methodology and provide educational estimates only — verify important figures with a qualified professional.