September 30, 2025

Scala – Automatic Derivation of Encoders and Decoders

In the previous two tutorials, we learnt how to build encoders and decoders. We used the forProductN(applicative-based decoders) and for comprehension (monadic decoders) to build decoders.

In this tutorial, we would talk about auto and semi-auto derivation of decoders. In this case, we would not have to write all the boiler-plate codes.

  1. Automatic Derivation of Encoders and Decoders
  2. Semi-Automatic Derivation
  3. Deriving Codecs
  4. Creating Custom Codec

 

1. Automatic Derivation

Automatic Derivation can be gotten from the implicit scope. To use it, you need to first add the following dependency to your build.sbt

libraryDependencies += "io.circe" %% "circe-generic" % "0.14.1"

 

Then you also need to import generic.auto from io.circe like so:

import io.circe.generic.auto._

Once we do this, circe would derive for us encoders and decoders for any case class whose fields also has encoders and decoders. This also works with nested case classes as well.

So assuming we have the following classes just like before:

//The Author case class
case class Author(
                   name: String,
                   bio: Option[String]
                 )

//The Article case class
case class Article(
                    id: UUID,
                    title: String,
                    content: String,
                    author: Author
                  )

 

Then we can simply have all the encoders and decoders from the implicit scope like so:

implicitly[Encoder[Author]]
implicitly[Decoder[Author]]

implicitly[Encoder[Article]]
implicitly[Decoder[Article]]

 

Then we can just encode and decode as usual using the encoders and decoders.

 

2. Semi-Automatic Derivation

Semi-Automatic derivation uses macros to provide you with encoders and decoders for your type without having to write them manually. However, it requires you to ask for Codec explicitly for any type you want to work with.

The import is:

import io.circe.generic.semiauto._

 

With this, if you want to have a user class for instance

case class Person(
               firstName: String,
               lastname: String,
               email: String
               )

 

We can then ask for encoder and decoder for the User type like so:

//Request for encoder and decoder for the Person type
implicit val personDecoder: Decoder[Person] = deriveDecoder[Person]
implicit val personEncoder: Encoder[Person]  = deriveEncoder[Person]

 

3. Deriving Codecs

You can also derive a Codec, which is a combination of Encoder and Decoder in a single structure. You can create a codec for Person using the line below:

implicit val personCodec: Codec[Person] = deriveCodec[Person]

 

4. Custom Codecs

We want to build an encoder for case classes User and YouTubeVideo. The YouTubeVideo class is a wrapper class around single property, video id. The User is a class with two properties: name and YouTubeVideo.

What we want to achieve is to serialise the User class such that the output would give name and an video url (built from the video id like http://yoube.com/watch?v={videoId}.

The code below shows an Encoder that modifies the value of one of the fields

object MyCodec {
  
  implicit val videoEncoder: Encoder[YoutubeVideo] = video =>
    Json.fromString(s"http://www.youtube.com/watch?v=${video.id}")

}

 

Leave a Reply