Convert JSON to Ruby Struct, Data.define or plain classes with from_h and to_h
A free JSON to Ruby converter: paste JSON and get Ruby classes back instantly. Choose a keyword-init Struct, an immutable Data.define (Ruby 3.2+) or a plain class with attr_accessor, each with from_h, from_json, to_h and to_json so it reads and writes the hash JSON.parse gives you. Types are inferred from the data and written as YARD comments, nested objects become their own classes, and members that would shadow a core method or a keyword are renamed so the file loads without warnings. Conversion runs entirely in your browser, so your JSON is never uploaded to a server.
Output formats: Struct (Keyword-init Struct with mutable members), Data (Immutable Data.define value object (Ruby 3.2+)), Class (Plain class with attr_accessor and a keyword constructor).
| Option | What it does |
|---|---|
| Root Class Name | Names the top-level Class. |
| Module (optional) | Wraps the classes in nested module blocks. Acme::Models, acme.models and acme/models all give module Acme, then module Models. |
| Field Naming Convention | One of snake_case, camelCase, Preserve Original. Defaults to snake_case. |
| Type for null values | Either Object or String, used when a key is null in every sample. |
| Separate Classes | Write each class to its own .rb file, each requiring the files it refers to with require_relative. Off (the default) puts every class in one file, so Copy All pastes into a single file that loads. |
| Map original JSON keys | Read and write each member under the original JSON key. Without it a member only binds to a key spelled exactly like it, so a camelCase key is not read into a snake_case member. |
| JSON value | Ruby type |
|---|---|
| string | String |
| integer | Integer |
| decimal | Float |
| true / false | Boolean (YARD) |
| ISO-8601 timestamp | String |
| array | Array<T> |
| object | nested class |
| null or missing in some samples | T, nil |
| null | Object (configurable) |
{
"id": 1,
"product_name": "Wireless Mouse",
"in_stock": true,
"tags": ["electronics"]
}
# @!attribute [rw] id
# @return [Integer]
# @!attribute [rw] product_name
# @return [String]
# @!attribute [rw] in_stock
# @return [Boolean]
# @!attribute [rw] tags
# @return [Array<String>]
Product = Struct.new(:id, :product_name, :in_stock, :tags, keyword_init: true) do
def self.from_h(hash)
return nil if hash.nil?
new(
id: hash['id'],
product_name: hash['product_name'],
in_stock: hash['in_stock'],
tags: hash['tags']
)
end
def self.from_json(json)
from_h(JSON.parse(json))
end
def to_h
{
'id' => id,
'product_name' => product_name,
'in_stock' => in_stock,
'tags' => tags
}
end
def to_json(*args)
to_h.to_json(*args)
end
end
Paste your JSON into the input pane and the Ruby classes appear beside it immediately. Set the root class name and an optional module, pick Struct, Data or Class, then copy the code or download it. Load it and call Root.from_json(json_string) to get an object, and to_json to write it back. Nested JSON objects become their own classes and arrays are mapped element by element.
Struct gives mutable members with a keyword constructor and works on every Ruby 2.5 and later. Data.define gives an immutable value object with the same API but needs Ruby 3.2 or later. A plain class has attr_accessor members and works anywhere. All three expose from_h, from_json, to_h and to_json, so you can switch without touching the calling code.
Ruby has no schema-driven deserializer in the standard library, so the classes read the Hash that JSON.parse returns. Each from_h builds the object member by member and hands nested objects to their own class, which keeps the code dependency-free: it needs only the json standard library.
A member named class, end, hash, to_h, members and so on would break the file or replace a method Ruby relies on, so it gets a trailing underscore (class_). The JSON key is kept: with Map original JSON keys on, from_h reads and to_h writes the original key.
Time.parse and DateTime.parse reformat a timestamp when it is written back, adding fractional digits or changing the zone, so the document would no longer round-trip. A timestamp stays a String; parse it where you need a Time.
No. Parsing, type inference, and code generation all run in your browser using JavaScript. Your JSON is never sent to a remote server, and there is no account to create. The page keeps your last input and settings in your own browser (localStorage) so they survive a reload; nothing is stored anywhere else.
The conversion itself never transmits your JSON, so a confidential payload does not leave your machine. You can verify this: open your browser DevTools, switch to the Network tab, and convert something — no request carries your data. Two things to know: your last input is saved in your browser's localStorage (clear site data to remove it), and the Copy Link button embeds your JSON in the link, so anyone who opens that link, and the server that receives the request, can see it. Only share such links where you are comfortable with that.
No. The generator is free to use with no sign-up and no usage limits.
Each nested JSON object becomes its own named type, derived from the key it appeared under. Arrays of objects are widened across every element, so a field that is a whole number in one element and a fraction in another gets a type that holds both.