Convert JSON to Scala 3 case classes with circe, play-json or upickle codecs
A free JSON to Scala converter: paste JSON and get Scala 3 case classes back instantly, each with its codec in the companion object. Pick the JSON library the codecs are written for: circe (Decoder and Encoder), play-json (Reads and Writes through a Format) or upickle (a ReadWriter over ujson.Value). Field types are inferred from the data (Int, Long, Double, Boolean, String, List and Option), nested objects become their own case classes, and keys that can be null or missing become Option fields that default to None. Names Scala reserves are renamed or put in backticks so the file compiles without warnings. Conversion runs entirely in your browser, so your JSON is never uploaded to a server.
Output format: Case class — Immutable case class with a codec in its companion.
| Option | What it does |
|---|---|
| Root Class Name | Names the top-level Case class. |
| Package (optional) | Emitted as a package clause at the top of each file. |
| Field Naming Convention | One of camelCase, PascalCase, snake_case, Preserve Original. Defaults to camelCase. |
| Type for null values | Either Json (JsValue / ujson.Value) or String, used when a key is null in every sample. |
| Separate Classes | Write each class to its own .scala file. Off (the default) puts every class in one file, so Copy All pastes into a single file that compiles. |
| Map original JSON keys | Read and write each field under the original JSON key (a @key annotation for upickle). Without it a field only binds to a key spelled exactly like it, so a snake_case key is not read into a camelCase field. |
| JSON library | One of circe, play-json or upickle; picks which JSON library the generated code is written for. |
| JSON value | Scala type |
|---|---|
| string | String |
| integer | Int |
| integer beyond 32 bits | Long |
| decimal | Double |
| true / false | Boolean |
| ISO-8601 timestamp | String |
| array | List[T] |
| object | nested case class |
| null or missing in some samples | Option[T] = None |
| null | Json / JsValue / ujson.Value (configurable) |
{
"id": 1,
"product_name": "Wireless Mouse",
"tags": ["electronics"]
}
final case class Product(id: Int, productName: String, tags: List[String])
object Product {
given Decoder[Product] = Decoder.instance { c =>
for {
v0 <- c.get[Int]("id")
v1 <- c.get[String]("product_name")
v2 <- c.get[List[String]]("tags")
} yield Product(v0, v1, v2)
}
given Encoder[Product] = Encoder.instance { x =>
Json.obj(
"id" -> Encoder[Int].apply(x.id),
"product_name" -> Encoder[String].apply(x.productName),
"tags" -> Encoder[List[String]].apply(x.tags)
)
}
}
Paste your JSON into the input pane and the Scala case classes appear beside it immediately. Set the root class name and an optional package, pick the JSON library, then copy the code or download it. With circe, parse the text and call .as[Root]; with play-json, Json.parse(text).as[Root]; with upickle, read[Root](text). Nested JSON objects become their own case classes and arrays become List[T].
circe is the most widely used in typed functional Scala and gives precise decoding errors; play-json is the usual pick in Play applications; upickle is small and fast and common in scripts and Scala.js. Each class carries a hand-written codec in its companion object, so no macro derivation or implicit configuration is involved and there is no limit on the number of fields. The case classes are the same for all three apart from the codec.
A key that is null or missing in at least one sample becomes an Option field that defaults to None, so a payload that omits it still decodes. A plain field makes the decoder fail when the key is absent.
Derivation needs a macro (circe-generic, Json.format, upickle macroRW), and those cannot rename a key per field without extra configuration or cannot expand a class with more than a couple of dozen fields; upickle fails to compile one at about 25 fields. A hand-written codec in the companion is a few lines per field, compiles at any size, and makes the key each field reads and writes explicit.
A field named class, type or val is written in backticks (`class`), which is how Scala spells a reserved word as a name. Names that would clash with a member every case class has, such as hashCode or copy, get a Value suffix. The JSON key is kept: with Map original JSON keys on, the codec reads and writes the original key.
No date type round-trips every timestamp through all three libraries: java.time reformats the fractional digits and the zone when it writes a value back. A String always round-trips exactly; parse it with java.time where you need a date.
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.