The Scala Toolkit

How to deserialize JSON to an object?

Language

Using Scala CLI, you can require the entire toolkit in a single line:

//> using toolkit latest

Alternatively, you can require just a specific version of UPickle:

//> using dep com.lihaoyi::upickle:3.1.0

In your build.sbt file, you can add the dependency on the Toolkit:

lazy val example = project.in(file("example"))
  .settings(
    scalaVersion := "3.2.2",
    libraryDependencies += "org.scala-lang" %% "toolkit" % "0.1.7"
  )

Alternatively, you can require just a specific version of UPickle:

libraryDependencies += "com.lihaoyi" %% "upickle" % "3.1.0"

In your build.sc file, you can add the dependency to the upickle library:

object example extends ScalaModule {
  def scalaVersion = "3.2.2"
  def ivyDeps =
    Agg(
      ivy"org.scala-lang::toolkit:0.1.7"
    )
}

Alternatively, you can require just a specific version of UPickle:

ivy"com.lihaoyi::upickle:3.1.0"

Parsing vs. deserialization

Parsing with uJson only accepts valid JSON, but it does not validate that the names and types of fields are as expected.

Deserialization, on the other hand, transforms a JSON string to some user-specified Scala data type, if required fields are present and have the correct types.

In this tutorial, we show how to deserialize to a Map and also to a custom case class.

Deserializing JSON to a Map

For a type T, uPickle can deserialize JSON to a Map[String, T], checking that all fields conform to T.

We can for instance, deserialize to a Map[String, List[Int]]:

val json = """{"primes": [2, 3, 5], "evens": [2, 4, 6]} """
val map: Map[String, List[Int]] =
  upickle.default.read[Map[String, List[Int]]](json)

println(map("primes"))
// prints: List(2, 3, 5)

If a value is the wrong type, uPickle throws a upickle.core.AbortException.

val json = """{"name": "Peter"} """
upickle.default.read[Map[String, List[Int]]](json)
// throws: upickle.core.AbortException: expected sequence got string at index 9

Deserializing JSON to a custom data type

In Scala, you can use a case class to define your own data type. For example, to represent a pet owner, you might:

case class PetOwner(name: String, pets: List[String])

To read a PetOwner from JSON, we must provide a ReadWriter[PetOwner]. uPickle can do that automatically:

import upickle.default._

implicit val ownerRw: ReadWriter[PetOwner] = macroRW[PetOwner]

Some explanations:

  • An implicit val is a value that can be automatically provided as an argument to a method or function call, without having to explicitly pass it.
  • macroRW is a method provided by uPickle that can generate a instances of ReadWriter for case classes, using the information about its fields.
import upickle.default.*

case class PetOwner(name: String, pets: List[String])
  derives ReadWriter

The derives keyword is used to automatically generate given instances. Using the compiler’s knowledge of the fields in PetOwner, it generates a ReadWriter[PetOwner].

This means that you can now read (and write) PetOwner objects from JSON with upickle.default.read(petOwner).

Notice that you do not need to pass the instance of ReadWriter[PetOwner] explicitly to the read method. But it does, nevertheless, get it from the context, as “given” value. You may find more information about contextual abstractions in the Scala 3 Book.

Putting everything together you should get:

import upickle.default._

case class PetOwner(name: String, pets: List[String])
implicit val ownerRw: ReadWriter[PetOwner] = macroRW

val json = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
val petOwner: PetOwner = read[PetOwner](json)

val firstPet = petOwner.pets.head
println(s"${petOwner.name} has a pet called $firstPet")
// prints: Peter has a pet called Toolkitty
import upickle.default.*

case class PetOwner(name: String, pets: List[String]) derives ReadWriter

val json = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
val petOwner: PetOwner = read[PetOwner](json)

val firstPet = petOwner.pets.head
println(s"${petOwner.name} has a pet called $firstPet")
// prints: Peter has a pet called Toolkitty

Contributors to this page: