FormatterHQ

JSON to Crystal Class Generator

Generate Crystal classes that include JSON::Serializable, with typed properties, nilable types for optional values and nested classes.

Input JSON
Output RUBY
Ready.

How to use the JSON to Crystal

  1. Paste your JSON sample (an object, or an array of objects) into the Input editor, or use Sample, Upload file or Load URL, or press Sample to try one.
  2. Set the Top-level class name, for example Task; it is converted to PascalCase. Nested types are named after their property keys.
  3. The Types only option has no effect for Crystal: classes always include JSON::Serializable, which is all Crystal needs.
  4. Press Generate (it also re-runs automatically as you edit), then use Copy or Download.

JSON to Crystal: JSON::Serializable classes

Crystal has JSON support in its standard library. Including the JSON::Serializable module in a class generates from_json and to_json at compile time, based on the declared property types. This JSON to Crystal generator writes those classes from a sample, so you get compile-time checked models with no runtime reflection and no extra shards.

Example

{ "id": 42, "title": "Write docs", "done": false,
  "owner": { "name": "Ada", "email": null },
  "subtasks": [ { "title": "Outline", "estimate": 2 }, { "title": "Draft" } ] }
require "json"

class Task
  include JSON::Serializable

  property id : Int32
  property title : String
  property done : Bool
  property owner : Owner
  property subtasks : Array(Subtask)
end

class Subtask
  include JSON::Serializable

  property title : String
  property estimate : Int32?
end

Int32? is Crystal shorthand for Int32 | Nil; missing or null keys decode as nil. The output above is shown with blank lines between properties removed.

How types are inferred

Crystal has real union types, and the generator uses them. Decimals become Float64, booleans Bool, arrays Array(T), and an array that mixes numbers and strings gets an alias such as alias X = Int32 | String. A key that is missing from some objects, or sometimes null, becomes nilable with ?. The compiler then forces you to handle the nil case before using the value, so the generated types turn gaps in your data into compile errors rather than runtime crashes. Paste a representative array of records to get the most accurate unions.

Using the classes

task = Task.from_json(File.read("task.json"))
puts task.subtasks.first.title
puts task.to_json

If the JSON does not match, from_json raises JSON::SerializableError naming the property, which makes API changes easy to diagnose.

Things to adjust

  • Integer size: whole numbers are typed Int32. For IDs or timestamps that can exceed about 2.1 billion, change them to Int64; otherwise parsing overflows.
  • Dates are kept as String. Crystal's Time works with JSON through a converter: @[JSON::Field(converter: Time::Format.new("%FT%T%z"))].
  • Renaming: keys that are not valid Crystal names, or that you want in snake_case, use @[JSON::Field(key: "userName")] above the property.
  • Unknown keys are ignored by default; include JSON::Serializable::Strict to raise on them.
  • Use getter instead of property for read-only models.

Crystal's syntax is close to Ruby's; for a Ruby version of the same models see JSON to Ruby. Validate your input with the JSON Validator. Everything runs in your browser.

Parsing arrays, maps and loose JSON

The generated class describes one record, but the same class works for other top-level shapes without any changes:

tasks = Array(Task).from_json(File.read("tasks.json"))
by_id = Hash(String, Task).from_json(%({"a1": {...}}))
any   = JSON.parse(raw)   # JSON::Any when no class fits

Use Array(Task) when the API returns a list and Hash(String, Task) when records are keyed by an ID. JSON.parse returns an untyped JSON::Any, which is handy for exploring a response but gives up the compile-time checks that make the generated classes worthwhile.

Frequently asked questions

Do I need a shard to use the generated Crystal code?
No. JSON::Serializable is part of Crystal’s standard library; require "json" is enough.
Why are integers Int32?
The generator uses Int32, Crystal’s default integer type. Change it to Int64 for large values such as database IDs or millisecond timestamps.
How do I map a JSON key with a different name?
Add an annotation above the property: @[JSON::Field(key: "originalKey")].
Can the classes be serialized back to JSON?
Yes. JSON::Serializable provides to_json as well as from_json.
What happens if a key is missing from the JSON?
For a nilable property such as Int32? the value becomes nil. For a non-nilable property, from_json raises JSON::SerializableError, unless you give the property a default value like property done : Bool = false.

Last updated September 26, 2026

Related tools

XML to Crystal Generate Crystal classes with JSON mapping from a XML sample instantly, with nested types detected automatically. JSON to C# Generate C# classes with JsonProperty attributes from a JSON sample instantly, with nested types detected automatically. JSON to Dart Generate Dart classes with fromJson and toJson from a JSON sample instantly, with nested types detected automatically. JSON to Elm Generate Elm type aliases with decoders from a JSON sample instantly, with nested types detected automatically. JSON to Flow Generate Flow Flow type definitions from a JSON sample instantly, with nested types detected automatically. JSON to Go Generate Go structs with json tags from a JSON sample instantly, with nested types detected automatically. JSON to Haskell Generate Haskell records with Aeson instances from a JSON sample instantly, with nested types detected automatically. JSON to Java Generate Java POJO classes with getters and setters from a JSON sample instantly, with nested types detected automatically. JSON to JavaScript PropTypes Generate JavaScript PropTypes React PropTypes definitions from a JSON sample instantly, with nested types detected automatically. JSON to JSON Schema Generate JSON Schema a JSON Schema document from a JSON sample instantly, with nested types detected automatically. JSON to Kotlin Generate Kotlin data classes from a JSON sample instantly, with nested types detected automatically. JSON to Laravel Model & Migration Generate a Laravel Eloquent model ($fillable, casts, relations) and a matching migration from a sample JSON record.