Circe uses Encoder and Decoder type classes for encoding and decoding. An Encoder[A] instance provides a function
that will convert any A to a Json and a Decoder[A] takes a Json value to return either an exception or an A. Circe provides
implicit instances of these type classes for many types from the Scala standard library, including Int, String, and others.
It also provides instances for List[A], Option[A], and other generic types, but only if A has an Encoder instance.
Encoding data to Json can be done using the .asJson syntax.
import io.circe.syntax._
val intsJson = List(1, 2, 3).asJson
// intsJson: io.circe.Json =
// [
// 1,
// 2,
// 3
// ]Use the .as syntax for decoding data from Json:
intsJson.as[List[Int]]
// res0: io.circe.Decoder.Result[List[Int]] = Right(List(1, 2, 3))The decode function from the included [parser] module can be used to directly decode a JSON String
import io.circe.parser.decodeLet's decode a JSON String:
val decodeList = decode[List[Int]]("[1, 2, 3]")
decodeList.isRight should be(res0)
decodeList should be(res1)
Sometimes it's convenient to have an Encoder or Decoder defined in your code, and semi-automatic derivation can help. You´d write:
import io.circe._, io.circe.generic.semiauto._
case class Foo(a: Int, b: String, c: Boolean)
implicit val fooDecoder: Decoder[Foo] = deriveDecoder[Foo]
implicit val fooEncoder: Encoder[Foo] = deriveEncoder[Foo]Or simply:
implicit val fooDecoder: Decoder[Foo] = deriveDecoder
implicit val fooEncoder: Encoder[Foo] = deriveEncoder The circe-generic projects includes a @JsonCodec annotation that simplifies the use of semi-automatic generic derivation:
import io.circe.generic.JsonCodec, io.circe.syntax._
@JsonCodec case class Bar(i: Int, s: String)
Bar(13, "Qux").asJson
// res4: io.circe.Json =
// {
// "i" : 13,
// "s" : "Qux"
// } This works both case classes and sealed trait hierarchies.
NOTE: You will need the Macro Paradise plugin to use annotation macros like @JsonCodec
It's also possible to construct encoders and decoders for case class-like types in a relatively boilerplate-free way without generic derivation:
case class User(id: Long, firstName: String, lastName: String)
object UserCodec {
implicit val decodeUser: Decoder[User] =
Decoder.forProduct3("id", "first_name", "last_name")(User.apply)
implicit val encodeUser: Encoder[User] =
Encoder.forProduct3("id", "first_name", "last_name")(u =>
(u.id, u.firstName, u.lastName))
} It’s not as clean or as maintainable as generic derivation, but it’s less magical, it requires nothing but circe-core, and if you need a custom name
mapping it’s currently the best solution (although 0.6.0 introduces experimental configurable generic derivation in the circe-generic-extras module).
It is also possible to derive an Encoder and Decoder for many types with no boilerplate at all.
Circe uses shapeless to automatically derive the necessary type class instances:
Let´s see what happens when we create a Json with derived fields
For this example we need to import io.circe.generic.auto._
case class Person(name: String)
case class Greeting(salutation: String, person: Person, exclamationMarks: Int)
val greetingJson = Greeting("Hey", Person("Chris"), 3).asJson
greetingJson.hcursor.downField("person").downField("name").as[String] should be(res0)