Circe serialization reference
Polymorphism and time
Notes:
- Data classes cannot be polymorphic
The following example demonstrates how polymorphism and time values are handled:
import java.time._
import io.circe._
import io.circe.parser._
import io.circe.syntax._
import io.circe.generic.semiauto._
import io.circe.generic.decoding._
import io.circe.generic.encoding._
import io.circe.java8.time._
trait Polymorphic
final case class TestPayload(
                        zonedDateTime: ZonedDateTime = ZonedDateTime.now()
                        , utcZonedDateTime: ZonedDateTime = ZonedDateTime.now(ZoneId.of("UTC"))
                        , localDateTime: LocalDateTime = LocalDateTime.now()
                        , localTime: LocalTime = LocalTime.now()
                        , localDate: LocalDate = LocalDate.now()
                      ) extends Polymorphic
object TestPayload {
  implicit val encodeTestPayload: Encoder[TestPayload] = deriveEncoder[TestPayload]
  implicit val decodeTestPayload: Decoder[TestPayload] = deriveDecoder[TestPayload]
}
final case class AnotherPayload(message: String) extends Polymorphic
object AnotherPayload {
  implicit val encodeAnotherPayload: Encoder[AnotherPayload] = deriveEncoder[AnotherPayload]
  implicit val decodeAnotherPayload: Decoder[AnotherPayload] = deriveDecoder[AnotherPayload]
}
object Polymorphic {
    implicit val encodePolymorphic: Encoder[Polymorphic] = Encoder.instance { c =>
    c match {
      case v: TestPayload =>
        Map("com.test#TestPayload" -> v).asJson
      case v: AnotherPayload =>
        Map("com.test#RealPayload" -> v).asJson
    }
  }
  implicit val decodePolymorphic: Decoder[Polymorphic] = Decoder.instance(c => {
    val fname = c.keys.flatMap(_.headOption).toSeq.head
    val value = c.downField(fname)
    fname match {
      case "com.test#TestPayload" =>
        value.as[TestPayload]
      case "com.test#RealPayload" =>
        value.as[AnotherPayload]
    }
  })
}
def test(t: Polymorphic): Unit = {
  val encoded = t.asJson.noSpaces
  println(s"Encoded:\n$encoded\n")
  val parsed = parse(encoded)
  println(s"Parsed:\n$parsed\n")
  val restored = parsed.map(_.as[Polymorphic])
  println(s"Restored:\n$restored\n")
}
test(TestPayload())
test(AnotherPayload("hi"))
This example produces the following output:
TestPayload:
Encoded:
{"TestPayload":{"zonedDateTime":"2018-04-02T22:34:31.367649+01:00[Europe/Dublin]","utcZonedDateTime":"2018-04-02T21:34:31.367744Z[UTC]","localDateTime":"2018-04-02T22:34:31.36778","localTime":"22:34:31.367813","localDate":"2018-04-02"}}
Parsed:
Right({
  "TestPayload" : {
    "zonedDateTime" : "2018-04-02T22:34:31.367649+01:00[Europe/Dublin]",
    "utcZonedDateTime" : "2018-04-02T21:34:31.367744Z[UTC]",
    "localDateTime" : "2018-04-02T22:34:31.36778",
    "localTime" : "22:34:31.367813",
    "localDate" : "2018-04-02"
  }
})
Restored:
Right(Right(TestPayload(2018-04-02T22:34:31.367649+01:00[Europe/Dublin],2018-04-02T21:34:31.367744Z[UTC],2018-04-02T22:34:31.367780,22:34:31.367813,2018-04-02)))
AnotherPayload:
Encoded:
{"RealPayload":{"message":"hi"}}
Parsed:
Right({
  "RealPayload" : {
    "message" : "hi"
  }
})
Restored:
Right(Right(AnotherPayload(hi)))
Algebraic types
Notes:
- Works same way as polymorphic types
- Use short names instead of fully qualified names
- You may introduce a local alias for an algebraic type member: adt MyAdt { domain1.A as A1 | A}. This allows you to resolve name conflicts
Cogen example for ADTs:
import java.time._
import io.circe._
import io.circe.parser._
import io.circe.syntax._
import io.circe.generic.semiauto._
import io.circe.generic.decoding._
import io.circe.generic.encoding._
import io.circe.java8.time._
sealed trait Algebraic
object NS1 {
  case class Payload(localTime: LocalTime = LocalTime.now()) extends Algebraic
  object Payload {
    implicit val encodeTestPayload: Encoder[Payload] = deriveEncoder[Payload]
    implicit val decodeTestPayload: Decoder[Payload] = deriveDecoder[Payload]
  }
}
object NS2 {
  case class AnotherPayload(message: String) extends Algebraic
  object AnotherPayload {
    implicit val encodeAnotherPayload: Encoder[AnotherPayload] = deriveEncoder[AnotherPayload]
    implicit val decodeAnotherPayload: Decoder[AnotherPayload] = deriveDecoder[AnotherPayload]
  }
}
object Algebraic {
  implicit val encodePolymorphic: Encoder[Algebraic] = deriveEncoder[Algebraic]
  implicit val decodePolymorphic: Decoder[Algebraic] = deriveDecoder[Algebraic]
}
def test(t: Algebraic): Unit = {
  val encoded = t.asJson.noSpaces
  println(s"Encoded:\n$encoded\n")
  val parsed = parse(encoded)
  println(s"Parsed:\n$parsed\n")
  val restored = parsed.map(_.as[Algebraic])
  println(s"Restored:\n$restored\n")
}
test(NS1.Payload())
test(NS2.AnotherPayload("hi"))
Output:
Encoded:
{"Payload":{"localTime":"18:13:31.942072"}}
Parsed:
Right({
  "Payload" : {
    "localTime" : "18:13:31.942072"
  }
})
Restored:
Right(Right(Payload(18:13:31.942072)))
Encoded:
{"AnotherPayload":{"message":"hi"}}
Parsed:
Right({
  "AnotherPayload" : {
    "message" : "hi"
  }
})
Restored:
Right(Right(AnotherPayload(hi)))
Identifiers
Identifiers codec just invokes .toString and .parse to serialize/deserialize Identifiers.
Please check identifier codegen example for additional details.
Full example:
final case class CompanyId(value: java.util.UUID) {
  override def toString: String = {
    import izumi.idealingua.runtime.model.IDLIdentifier._
    val suffix = Seq(this.value).map(part => escape(part.toString)).mkString(":")
    s"CompanyId#$suffix"
  }
}
trait CompanyIdCirce {
  import _root_.io.circe.{ Encoder, Decoder }
  implicit val encodeCompanyId: Encoder[CompanyId] = Encoder.encodeString.contramap(_.toString)
  implicit val decodeCompanyId: Decoder[CompanyId] = Decoder.decodeString.map(CompanyId.parse)
}
object CompanyId extends CompanyIdCirce {
  def parse(s: String): CompanyId = {
    import izumi.idealingua.runtime.model.IDLIdentifier._
    val withoutPrefix = s.substring(s.indexOf("#") + 1)
    val parts = withoutPrefix.split(":").map(part => unescape(part))
    CompanyId(parsePart[java.util.UUID](parts(0), classOf[java.util.UUID]))
  }
  implicit class CompanyIdExtensions(_value: CompanyId)
}
Enumerations
Identifiers codec just invokes .toString and .parse same way as it implemented for Identifiers.
1.2.21*