Skip to content
AgileLogics
5 min read

JSON

Prerequisites: Type classes

Scala has several excellent JSON libraries. All map JSON to case classes through type classes, usually derived automatically.

upickle (simple and fast)

// illustrative
//> using dep com.lihaoyi::upickle:4.1.0
import upickle.default.*

case class Address(city: String, country: String) derives ReadWriter
case class User(name: String, age: Int, tags: List[String], address: Option[Address])
  derives ReadWriter

@main def json(): Unit =
  val ada = User("Ada", 36, List("admin"), Some(Address("London", "UK")))
  val text = write(ada)
  println(text)
  println(read[User](text) == ada)

  val parsed = ujson.read("""{"name": "Linus", "langs": ["C", "Rust"]}""")
  println(parsed("langs")(1).str)
{"name":"Ada","age":36,"tags":["admin"],"address":{"city":"London","country":"UK"}}
true
Rust

circe (Typelevel ecosystem)

// illustrative
//> using dep io.circe::circe-generic:0.14.12
//> using dep io.circe::circe-parser:0.14.12
import io.circe.*, io.circe.syntax.*, io.circe.parser.*

case class Order(id: Int, total: BigDecimal) derives Codec.AsObject

@main def circe(): Unit =
  println(Order(1, 59.97).asJson.noSpaces)
  println(decode[Order]("""{"id": 2, "total": "oops"}"""))
{"id":1,"total":59.97}
Left(DecodingFailure at .total: Got value '"oops"' with wrong type, expecting number)

Decoding returns an Either, so invalid JSON is an ordinary value to handle — no exceptions. Check sample payloads with the JSON Formatter or JSON Validator.

Choosing

  • upickle — minimal, fast, great for scripts and Scala CLI.
  • circe — the standard with Cats Effect, http4s, and Tapir.
  • zio-json — the ZIO ecosystem's choice.
  • jsoniter-scala — the fastest, using compile-time macros.