JSON in Scala — play-json, circe & spray-json
Scala does not include JSON support in its standard library, but three libraries dominate: play-json (part of the Play Framework ecosystem, widely used standalone), circe (functional, typeclass-based, excellent for cats users), and spray-json (lightweight, protocol-based).
play-json — parse and write JSON
Add com.typesafe.play:play-json to your build. Use Json.parse() to read and Json.stringify() to write.
import play.api.libs.json._
val jsonStr = """{"id":1,"name":"Alice","email":"alice@example.com"}"""
val json: JsValue = Json.parse(jsonStr)
// Read a field
val name = (json \ "name").as[String] // Alice
// Serialise back to string
val compact = Json.stringify(json)
val pretty = Json.prettyPrint(json)play-json — Reads and Writes for case classes
Define implicit Reads[T] and Writes[T] (or Format[T]) to convert between JSON and your case classes. The macro Json.format[T] generates both automatically.
import play.api.libs.json._
case class User(id: Int, name: String, email: String)
object User {
implicit val format: OFormat[User] = Json.format[User]
}
// Decode
val user: User = Json.parse(jsonStr).as[User]
// Encode
val out: JsValue = Json.toJson(user)
println(Json.stringify(out))circe — automatic derivation
Add io.circe:circe-core, circe-generic, and circe-parser. Import io.circe.generic.auto._ for automatic codec derivation from case classes.
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
case class User(id: Int, name: String, email: String)
// Decode — returns Either[Error, User]
val result: Either[Error, User] = decode[User](jsonStr)
result match {
case Right(user) => println(user.name) // Alice
case Left(err) => println(s"Error: $err")
}
// Encode
val user = User(1, "Alice", "alice@example.com")
println(user.asJson.noSpaces)circe — cursor-based field access
Use circe's HCursor to navigate and extract individual fields without decoding to a case class.
import io.circe.parser._
val cursor = parse(jsonStr).getOrElse(Json.Null).hcursor
val name = cursor.downField("name").as[String] // Right("Alice")
val id = cursor.downField("id").as[Int] // Right(1)
// Nested field
val city = cursor
.downField("address")
.downField("city")
.as[String]spray-json — protocol-based
Add io.spray:spray-json. Define a JsonFormat using jsonFormat helpers and import the DefaultJsonProtocol.
import spray.json._
import DefaultJsonProtocol._
case class User(id: Int, name: String, email: String)
object UserProtocol extends DefaultJsonProtocol {
implicit val userFormat: RootJsonFormat[User] = jsonFormat3(User)
}
import UserProtocol._
// Parse
val user: User = jsonStr.parseJson.convertTo[User]
// Serialise
val json: String = user.toJson.prettyPrintRelated Tools
Frequently Asked Questions
Which Scala JSON library should I use?▾
Use play-json if you are in the Play Framework ecosystem or need a battle-tested standalone library. Use circe if you are already using cats/cats-effect or want a purely functional, typeclass-based approach. Use spray-json for lightweight Akka HTTP services.
How do I parse JSON in Scala with circe?▾
Add circe-parser to your build and call io.circe.parser.decode[MyType](jsonString). It returns Either[io.circe.Error, MyType]. Import io.circe.generic.auto._ for automatic case class derivation.
How do I auto-generate JSON codecs for case classes in Scala?▾
With circe, import io.circe.generic.auto._ and Encoder/Decoder instances are automatically derived. With play-json, use Json.format[MyCaseClass] in the companion object. With spray-json, use jsonFormat(N) helpers.
How do I handle optional fields in Scala JSON?▾
Declare the field as Option[T] in your case class. All three major libraries (play-json, circe, spray-json) treat missing or null JSON keys as None and serialise None as an absent key (or null, depending on configuration).
How do I read a single field from JSON in Scala without a full case class?▾
With circe, use the HCursor API: parse(json).getOrElse(Json.Null).hcursor.downField("key").as[String]. With play-json, use (Json.parse(json) \ "key").as[String].
JSON in Other Languages
Format and validate your JSON instantly
Free, no ads, no sign-up. Also converts JSON to TypeScript, YAML, CSV, and more.
Open JSON Formatter →If jsondecode.com saved you time, share it with your team
Free forever. No ads. No sign-up. Help other developers find it.