Encoder[A]是将A类型转化成JSON的函数,Decoder[A]是将Json转化成一个A对象或者是exception的函数。
circe提供了scala标准库中类型的的implicit函数,可以方便的对String,Int等基本类型进行处理,同时也提供了List[A]、Option[A]和其他泛型类型的处理,只要是A有对应的Encoder。
可以使用 .asJson 将一个数据对象转化成Json对象:
import io.circe.syntax._
// import io.circe.syntax._
val intsJson = List(1, 2, 3).asJson
// intsJson: io.circe.Json =
// [
// 1,
// 2,
// 3
// ]
使用 .as 将一个Json对象转化成数据对象:
intsJson.as[List[Int]]
// res0: io.circe.Decoder.Result[List[Int]] = Right(List(1, 2, 3))
parser模块中的decode函数可以直接把json string转化成对象:
import io.circe.parser.decode
// import io.circe.parser.decode
decode[List[Int]]("[1, 2, 3]")
// res1: Either[io.circe.Error,List[Int]] = Right(List(1, 2, 3))
Semi-automatic Derivation
有时,在代码中定义一个Encoder或Encoder,半自动推导可以帮助更简单的实现,可以写成如下形式:
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]
或者是:
implicit val fooDecoder: Decoder[Foo] = deriveDecoder
implicit val fooEncoder: Encoder[Foo] = deriveEncoder
@JsonCodec
circe-generic模块提供了@JsonCodec注解用来简化上面的实现:
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
// res4: io.circe.Json =
// {
// "i" : 13,
// "s" : "Qux"
// }
这种注解方式对case classes和sealed trait有效。
注意: 你需要使用Macro Paradise插件用来调用了类似 @JsonCodec的注解
forProductN 方法
也可以在不使用通用derviation的情况下构建case class的Encoders和Decoders:
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)
)
}
这种方式不如generic derivation通用,但这种方式只依赖于circe-core
Fully automatic derivation
circe使用shapeless实现自动的推导对应的class实例:
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
// res6: io.circe.Json =
// {
// "salutation" : "Hey",
// "person" : {
// "name" : "Chris"
// },
// "exclamationMarks" : 3
// }
Custom encoders/decoders
如果你想自己实现而不是使用自动化或半自动化的derivation,可以通过一下几种方法实现:
首先,可以从头编写一个Encoder[A]和Decoder[A]:
class Thing()
// defined class Thing
implicit val encodeFoo: Encoder[Thing] = new Encoder[Thing] {
final def apply(a: Thing): Json = ??? // your implementation goes here
}
// encodeFoo: io.circe.Encoder[Thing] = $anon$1@4ef154df
implicit val decodeFoo: Decoder[Thing] = new Decoder[Thing] {
final def apply(c: HCursor): Decoder.Result[Thing] = Left(DecodingFailure("Not implemented yet", c.history))
}
// decodeFoo: io.circe.Decoder[Thing] = $anon$1@164a929d
但在许多情况下,您可能会发现它更方便搭载已经可用的Decoder。例如,java.time.Instance 的解码器可以这样写:
import cats.syntax.either._
// import cats.syntax.either._
import java.time.Instant
// import java.time.Instant
implicit val encodeInstant: Encoder[Instant] = Encoder.encodeString.contramap[Instant](_.toString)
// encodeInstant: io.circe.Encoder[java.time.Instant] = io.circe.Encoder$$anon$11@56e2186a
implicit val decodeInstant: Decoder[Instant] = Decoder.decodeString.emap { str =>
Either.catchNonFatal(Instant.parse(str)).leftMap(t => "Instant")
}
// decodeInstant: io.circe.Decoder[java.time.Instant] = io.circe.Decoder$$anon$21@419672fb
Custom key types
如果需要对Map [K,V]进行编码/解码,其中K不是String(或Symbol,Int,Long等),则需要为自定义键类型提供KeyEncoder和/或KeyDecoder。
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@423285bc
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@7edf5d5a
json.as[Map[Foo, Int]]
// res7: io.circe.Decoder.Result[Map[Foo,Int]] = Right(Map(Foo(hello) -> 123, Foo(world) -> 456))