I have the following code, that does not compile:
import java.time.Instant
import io.circe.{Decoder, Encoder}
import io.circe.generic.auto._
import io.circe.syntax._
trait SapHealth {}
case class SapHealthRejected(reason: String) extends SapHealth
case class SapHealthAccepted(sapId: String, requestedAt: Long) extends SapHealth
object SapHealth {
private val build: SapHealth = SapHealthAccepted(SapmockActor.system.name, Instant.now().getEpochSecond)
val create: String = build.asJson.noSpaces
implicit val encodeFieldType: Encoder[SapHealthAccepted] =
Encoder.forProduct2("sap-id", "requested_at")(SapHealthAccepted.unapply(_).get)
implicit val decodeFieldType: Decoder[SapHealthAccepted] =
Decoder.forProduct2("sap-id", "requested_at")(SapHealthAccepted.apply)
}
The compiler complains:
could not find implicit value for parameter encoder: io.circe.Encoder[com.sweetsoft.SapHealth]
[error] val create: String = build.asJson.noSpaces
What am I missing?
You've specifically up-cast build
to SapHealth
, but you don't provide an Encoder
instance for SapHealth
(only SapHealthAccepted
), and circe-generic can't derive one because you haven't sealed the trait hierarchy.
The most straightforward solution would be to add sealed
:
import io.circe.{Decoder, Encoder}
import io.circe.generic.auto._
import io.circe.syntax._
sealed trait SapHealth {}
case class SapHealthRejected(reason: String) extends SapHealth
case class SapHealthAccepted(sapId: String, requestedAt: Long) extends SapHealth
object SapHealth {
implicit val encodeFieldType: Encoder[SapHealthAccepted] =
Encoder.forProduct2("sap-id", "requested_at")(SapHealthAccepted.unapply(_).get)
implicit val decodeFieldType: Decoder[SapHealthAccepted] =
Decoder.forProduct2("sap-id", "requested_at")(SapHealthAccepted.apply)
private val build: SapHealth = SapHealthAccepted("foo", 123L)
val create: String = build.asJson.noSpaces
}
Note that you also need to rearrange the definitions to avoid running into null-pointer exceptions because of initialization order (if you put create
before encodeFieldType
, the derived SapHealth
encoder will try to use encodeFieldType
before it's initialized). With the rearrangement above, this works just fine:
scala> SapHealth.create
res2: String = {"SapHealthAccepted":{"sap-id":"foo","requested_at":123}}
Note that the derived SapHealth
encoder is using your custom SapHealthAccepted
encoder, which I assume is what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With