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.decode
Let'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)