Reputation: 2015
I am looking to provide JSON encoders for the following case class:
import io.circe.generic.extras.Configuration
final case class Hello[T](
source: String,
version: Int = 1,
data: T
)
object Hello {
implicit val configuration: Configuration = Configuration.default.withDefaults
}
I would ordinarily call deriveEncoder[A]
in the companion object, but that doesn't work here as there is no reference or Encoder
for T
available here.
The Hello
type will be provided to clients as a library, so I would like to do as much of the boilerplate as possible within this type rather than depend on client code providing the encoder and decoder. Is there an idiomatic solution to this with circe so that clients provide an encoder/decoder for T
and this gets used to derive the encoder/decoder for Hello[T]
?
Upvotes: 9
Views: 3721
Reputation: 149518
Yes, you need to add a context bound requiring an implicit encoder to be present for any type T
:
import io.circe.generic.semiauto._
final case class Hello[T](
source: String,
version: Int = 1,
data: T
)
object Hello {
implicit def helloEncoder[T: Encoder]: Encoder[Hello[T]] = deriveEncoder
}
Such that when the user creates their own Hello[Foo]
type, they'll have to make sure that Foo
has its own encoder.
Upvotes: 17
Reputation: 1
This also worked out for me, without using companion object , (case class for "Generic" JsonParserCirce[T] used for SCIO Apache Beam Pipeline)
import com.spotify.scio.coders.Coder
import com.spotify.scio.values.SCollection
import io.circe
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
import io.circe.parser.decode
import org.slf4j.{Logger, LoggerFactory}
import java.util.Locale
case class JsonParserCirce[T](implicit val lazyDecoder: Lazy[DerivedDecoder[T]]) {
implicit val coderLocale: Coder[Locale] = Coder.kryo[Locale]
implicit def defaultDecoder: Decoder[T] = deriveDecoder[T]
def parseJSONStrings(
messages: SCollection[String]
)(implicit coder: Coder[T]): (SCollection[T], SCollection[JsonError]) = {
log.info("Parsing JSON Strings...")
val jsons: SCollection[Either[circe.Error, T]] = messages.map { s: String => json2CaseClass(s) }
/*My Transformations*/
// String to Json using Circe
def json2CaseClass(jsonStr: String): Either[circe.Error, T] =
decode(jsonStr)
[spotify-scio] [apache-beam]
P.S.1: (I only need Decoder[T])
P.S.2: I got some inspiration here too https://github.com/circe/circe/issues/1442
P.S.3: (implicit val lazyDecoder: Lazy[DerivedDecoder[T]]) is needed othwerwise the compiler throws
could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[T] implicit def defaultDecoder: Decoder[T] = deriveDecoder[T]
Upvotes: 0