JSON to Ruby
Turn a JSON sample into Ruby you can paste straight into a file — a frozen hash literal for a fixture or a constant, a Struct for a lightweight value object, or a full class with attr_reader, from_json and to_h. Key names are converted to snake_case for the object forms, because userName is not a Ruby attribute name anyone wants to type.
Which output to choose
Hash literal reproduces the document as Ruby data. With symbol keys it reads the way Ruby code
normally does — { name: "ada" } — and keys that are not valid identifiers fall back to the
"key": value form, which Ruby 3 accepts. Turn symbols off for string keys, which is what you want
when the hash mirrors external data and the keys are arbitrary. This is the form for a test fixture or a
configuration constant.
Struct with keyword_init: true gives you a value object in three lines:
User.new(name: "ada"), with readers, equality and to_h for free. It is the least code
for the most behaviour, and the right choice when you need a type but not logic.
Class is the fuller scaffold — attr_reader for each field, a keyword
initialize with the inferred Ruby type in a comment beside each assignment, a
self.from_json that uses symbolize_names: true, and to_h /
to_json for the way back. Somewhere to hang validation and behaviour as the model grows.
What the inference can and cannot see
Fields come from one sample object — the top-level object, or the first object found in an array. A field that
is absent from that sample does not appear in the generated code, and a field that is null there
has no inferrable type, so its comment says nil. For a union of every field across a heterogeneous
array, generate a schema first and read the field list from that.
Nested objects are not turned into nested classes — they stay as hashes in the constructor. Ruby's duck typing makes that workable more often than it would be in a statically typed language, and generating a class tree from one sample tends to produce more code than it saves. If you want the nested types too, Schema → types covers the statically typed languages properly.
Privacy
Nothing is uploaded. The conversion runs in this tab using your own browser's JavaScript engine — no server sees your data, and the page keeps working with the network disconnected.
FAQ
Why are the keys converted to snake_case?
For the Struct and class forms, because they become method names and Ruby convention is snake_case — user.user_name, not user.userName. The hash literal keeps the original keys, since it is data rather than an interface. If you need the mapping in both directions, keep the original key in a constant beside the class.
Does from_json handle nested keys?
symbolize_names: true symbolises every key at every depth, so nested hashes arrive with symbol keys throughout. But the constructor only takes the top-level fields — a nested hash is assigned whole, not converted into another object.
What about Sorbet or RBS signatures?
Not generated here. Both are worth adding once the shape is settled, and both are easier to write from an inferred schema than from a sample.
Is frozen_string_literal necessary?
Not necessary, but conventional in modern Ruby and required by most linters. It makes every string literal in the file frozen, which is why the hash form also appends .freeze to the whole structure when the option is on.