sowen sowen - 9 months ago 89
Scala Question

How does circe parse a generic type object to Json?

Here is what I am trying to do

case class MessageModel (time: Long, content: String) {}
val message = MessageModel(123, "Hello World")
def jsonParser[A] (obj: A) : String = obj.asJson.noSpaces

println jsonParser[MessageModel](message)

this doesn't work, because it will complain
Error:(13, 8) could not find implicit value for parameter encoder: io.circe.Encoder[A] obj.asJson.noSpaces

I kind of understand why it is happening, but is there a way to work around it?


Answer Source

Encoding and decoding in circe are provided by type classes, which means that you have to be able to prove at compile time that you have a type class instance for A if you want to encode (or decode) a value of type A.

This means that when you write something like this:

import io.circe.syntax._

def jsonPrinter[A](obj: A): String = obj.asJson.noSpaces

You're not providing enough information about A for circe to be able to print values of that type. You can fix this with a context bound:

import io.circe.Encoder
import io.circe.syntax._

def jsonPrinter[A: Encoder](obj: A): String = obj.asJson.noSpaces

Which is Scala's syntactic sugar for something like this:

def jsonPrinter[A](obj: A)(implicit encoder: Encoder[A]): String =

Both of these will compile, and you can pass them a value of any type that has an implicit Encoder instance. For your MessageModel specifically, you could use circe's generic derivation, since it's a case class:

scala> import

scala> case class MessageModel(time: Long, content: String)
defined class MessageModel

scala> val message = MessageModel(123, "Hello World")
message: MessageModel = MessageModel(123,Hello World)

scala> jsonPrinter(message)
res0: String = {"time":123,"content":"Hello World"}

Note that this wouldn't work without the auto import, which is providing Encoder instances for any case class (or sealed trait hierarchy) whose members are all also encodeable.