Semi-automatic Derivation

Sometimes it's convenient to have an Encoder or Decoder defined in your code, and semi-automatic derivation can help. You'd write:

import io.circe._, io.circe.generic.semiauto._, io.circe.syntax._

case class Foo(a: Int, b: String, c: Boolean)

implicit val fooDecoder: Decoder[Foo] = deriveDecoder[Foo]
implicit val fooEncoder: Encoder[Foo] = deriveEncoder[Foo]

Foo(13, "Qux", false).asJson

You can also simplify to just write the last two lines as:

implicit val fooDecoder: Decoder[Foo] = deriveDecoder
implicit val fooEncoder: Encoder[Foo] = deriveEncoder

Specific case for Value Class

Most of the time, when using Value Class, we expect only the inner value in the serialized format.

It can be achieved using circe-generic-extras:

import io.circe._, io.circe.generic.extras.semiauto._

case class Foo(a: Int)

implicit val fooDecoder: Decoder[Foo] = deriveUnwrappedDecoder[Foo]
implicit val fooEncoder: Encoder[Foo] = deriveUnwrappedEncoder[Foo]

The expected serialization for Foo(123) is simply 123

@JsonCodec

The circe-generic project includes a @JsonCodec annotation that simplifies the use of semi-automatic generic derivation:

import io.circe.generic.JsonCodec, io.circe.syntax._

@JsonCodec case class Bar(i: Int, s: String)

Bar(13, "Qux").asJson
// res3: io.circe.Json = JObject(value = object[i -> 13,s -> "Qux"])

This works with both case classes and sealed trait hierarchies.

NOTE: You will need to use the -Ymacro-annotations flag to use annotation macros like @JsonCodec. (If you're using Scala 2.10.x to Scala 2.12.x you will need the Macro Paradise plugin instead).

forProductN helper methods

It's also possible to construct encoders and decoders for case class-like types in a relatively boilerplate-free way without generic derivation:

import io.circe.{ Decoder, Encoder }

case class User(id: Long, firstName: String, lastName: String)

implicit val decodeUser: Decoder[User] =
  Decoder.forProduct3("id", "first_name", "last_name")(User.apply)
// decodeUser: Decoder[User] = io.circe.ProductDecoders$$anon$5@6088f0c2

implicit val encodeUser: Encoder[User] =
  Encoder.forProduct3("id", "first_name", "last_name")(u =>
    (u.id, u.firstName, u.lastName)
  )
// encodeUser: Encoder[User] = io.circe.ProductEncoders$$anon$3@578b0659

It's not as clean or as maintainable as generic derivation, but it's less magical, it requires nothing but circe-core, and if you need a custom name mapping it's currently the best solution (although 0.6.0 introduces experimental configurable generic derivation in the generic-extras module).