CalcCafe

JSON to Crystal

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

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.

Related tools