Encoding and decoding
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 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.
Semi-automatic derivation
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
@JsonCodec
The circe-generic project includes a @JsonCodec annotation that simplifies the
use of semi-automatic generic derivation:
import io.circe.generic.JsonCodec, io.circe.syntax._
// import io.circe.generic.JsonCodec
// import io.circe.syntax._
@JsonCodec case class Bar(i: Int, s: String)
// defined class Bar
// defined object Bar
Bar(13, "Qux").asJson
// res2: io.circe.Json =
// {
// "i" : 13,
// "s" : "Qux"
// }
This works with both case classes and sealed trait hierarchies.
forProductN helper methods
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 (until configurable generic derivation is released in 0.6.0).
Fully automatic derivation
It is also possible to derive Encoders and Decoders for many types with no boilerplate at all.
circe uses shapeless to automatically derive the necessary type class instances:
import io.circe.generic.auto._
// import io.circe.generic.auto._
case class Person(name: String)
// defined class Person
case class Greeting(salutation: String, person: Person, exclamationMarks: Int)
// defined class Greeting
Greeting("Hey", Person("Chris"), 3).asJson
// res4: io.circe.Json =
// {
// "salutation" : "Hey",
// "person" : {
// "name" : "Chris"
// },
// "exclamationMarks" : 3
// }
Custom encoders/decoders
If you want to write your own codec instead of using automatic or semi-automatic derivation, you can do so in a couple of ways.
Firstly, you can write a new Encoder[A] and Decoder[A] from scratch:
import cats.data.Xor
// import cats.data.Xor
class Thing()
// defined class Thing
implicit val encodeFoo = new Encoder[Thing] {
final def apply(a: Thing): Json = ??? // your implementation goes here
}
// encodeFoo: io.circe.Encoder[Thing] = $anon$1@2b696634
implicit val decodeFoo = new Decoder[Thing] {
final def apply(c: HCursor): Decoder.Result[Thing] = Xor.left(DecodingFailure("Not implemented yet", c.history))
}
// decodeFoo: io.circe.Decoder[Thing] = $anon$1@7711f5a5
But in many cases you might find it more convenient to piggyback on top of the decoders that are
already available. For example, a codec for java.time.Instant might look like this:
import java.time.Instant
// import java.time.Instant
implicit val encodeInstant: Encoder[Instant] = Encoder.encodeString.contramap[Instant](i => i.toString)
// encodeInstant: io.circe.Encoder[java.time.Instant] = io.circe.Encoder$$anon$12@7ce531c6
implicit val decodeInstant: Decoder[Instant] = Decoder.decodeString.emap { str =>
Xor.catchNonFatal(Instant.parse(str)).leftMap(t => "Instant")
}
// decodeInstant: io.circe.Decoder[java.time.Instant] = io.circe.Decoder$$anon$21@6a568ca7
Custom key types
If you need to encode/decode Map[K, V] where K is not String (or Symbol, Int, Long, etc.),
you need to provide a KeyEncoder and/or KeyDecoder for your custom key type.
For example:
import io.circe.syntax._
// import io.circe.syntax._
case class Foo(value: String)
// defined class Foo
implicit val fooKeyEncoder = new KeyEncoder[Foo] {
override def apply(foo: Foo): String = foo.value
}
// fooKeyEncoder: io.circe.KeyEncoder[Foo] = $anon$1@3aff597
val map = Map[Foo, Int](
Foo("hello") -> 123,
Foo("world") -> 456
)
// map: scala.collection.immutable.Map[Foo,Int] = Map(Foo(hello) -> 123, Foo(world) -> 456)
val json = map.asJson
// json: io.circe.Json =
// {
// "hello" : 123,
// "world" : 456
// }
implicit val fooKeyDecoder = new KeyDecoder[Foo] {
override def apply(key: String): Option[Foo] = Some(Foo(key))
}
// fooKeyDecoder: io.circe.KeyDecoder[Foo] = $anon$1@3097096a
json.as[Map[Foo, Int]]
// res5: io.circe.Decoder.Result[Map[Foo,Int]] = Right(Map(Foo(hello) -> 123, Foo(world) -> 456))
Warnings and known issues
-
Please note that generic derivation will not work on Scala 2.10 unless you’ve added the Macro Paradise plugin to your build. See the quick start section on the home page for details.
-
Generic derivation may not work as expected when the type definitions that you’re trying to derive instances for are at the same level as the attempted derivation. For example:
scala> import io.circe.Decoder, io.circe.generic.auto._ import io.circe.Decoder import io.circe.generic.auto._ scala> sealed trait A; case object B extends A; object X { val d = Decoder[A] } defined trait A defined object B defined object X scala> object X { sealed trait A; case object B extends A; val d = Decoder[A] } <console>:19: error: could not find implicit value for parameter d: io.circe.Decoder[X.A] object X { sealed trait A; case object B extends A; val d = Decoder[A] }This is unfortunately a limitation of the macro API that Shapeless uses to derive the generic representation of the sealed trait. You can manually define these instances, or you can arrange the sealed trait definition so that it is not in the same immediate scope as the attempted derivation (which is typically what you want, anyway).
-
For large or deeply-nested case classes and sealed trait hierarchies, the generic derivation provided by the
genericsubproject may stack overflow during compilation, which will result in the derived encoders or decoders simply not being found. Increasing the stack size available to the compiler (e.g. withsbt -J-Xss64mif you’re using SBT) will help in many cases, but we have at least one report of a case where it doesn’t. -
More generally, the generic derivation provided by the
genericsubproject works for a wide range of test cases, and is likely to just work for you, but it relies on macros (provided by Shapeless) that rely on compiler functionality that is not always perfectly robust (“SI-7046 is like playing roulette”), and if you’re running into problems, it’s likely that they’re not your fault. Please file an issue here or ask a question on the Gitter channel, and we’ll do our best to figure out whether the problem is something we can fix. -
When using the
io.circe.generic.JsonCodecannotation, the following will not compile:import io.circe.generic.JsonCodec @JsonCodec sealed trait A case class B(b: String) extends A case class C(c: Int) extends AIn cases like this it’s necessary to define a companion object for the root type after all of the leaf types:
import io.circe.generic.JsonCodec @JsonCodec sealed trait A case class B(b: String) extends A case class C(c: Int) extends A object ASee this issue for additional discussion (this workaround may not be necessary in future versions).
-
circe’s representation of numbers is designed not to lose precision during decoding into integral or arbitrary-precision types, but precision may still be lost during parsing. This shouldn’t happen when using Jawn for parsing, but
scalajs.js.JSONparses JSON numbers into a floating point representation that may lose precision (even when decoding into a type likeBigDecimal; see this issue for an example).