scalacirce

How can I define some generic decoder/encoder with circe?


Suppose I have multiple types of ids defined as follows.

sealed trait Id {
  val value: String
}
case class IdA(value: String) extends Id
case class IdB(value: String) extends Id
case class IdC(value: String) extends Id

These classes should be decoded from and encoded to the following JSON.

 {
    "id: "some-id"
 }

How can I define some generic decoder/encoder for the id,

case class A(id: IdA, name: String, count: Int)
case class B(id: IdB, name: String, count: Int)
case class C(id: IdC, name: String, count: Int)

so that the classes above can be decoded from and encoded to the JSON below?

{
   "id" : "some-id",
   "name": "some-name",
   "count": 2
}

if possible, I want the id field to be flexible for both decoder and encoder that is id maybe "id-x" in one case and "id-y" in another.


Solution

  • I have solved it by simply defining the following decoder and encoder.

    import cats.implicits._
    import io.circe.{Decoder, Encoder}
    import shapeless._
    
     implicit def encoderValueClass[T <: Id, V](implicit
          g: Lazy[Generic.Aux[T, V :: HNil]],
          e: Encoder[V]
      ): Encoder[T] = Encoder.instance { value =>
        e(g.value.to(value).head)
      }
    
      implicit def idDecoder[T <: Id, V](implicit
          g: Lazy[Generic.Aux[T, V :: HNil]],
          d: Decoder[V]
      ): Decoder[T] = Decoder.instance { cursor =>
        d(cursor).map { value =>
          g.value.from(value :: HNil)
        }
      }