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