Skip to content

Instantly share code, notes, and snippets.

@AlexBaitov
Last active October 20, 2018 16:28
Show Gist options
  • Select an option

  • Save AlexBaitov/25696fc9cfada944a8a9058ae3e1e940 to your computer and use it in GitHub Desktop.

Select an option

Save AlexBaitov/25696fc9cfada944a8a9058ae3e1e940 to your computer and use it in GitHub Desktop.
io.circe - Enconding and decoding - Examples

Encoding AnyVal

Initial data

case class RubricId(value: Int) extends AnyVal

Desired result

import io.circe.syntax._

RubricId(1).asJson
{
  "id": 1
}

Reflection

import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder

implicit val encoderRubricId: Encoder[RubricId] = deriveEncoder[RubricId]
{
  "id": {
    "value": 1
  }
}

Macros + Reflection

import io.circe.generic.JsonCodec

@JsonCodec(encodeOnly = true) case class RubricId(value: Int) extends AnyVal
{
  "id": {
    "value": 1
  }
}

Custome encoder (concrete)

import io.circe.Encoder

implicit val encRubricId: Encoder[RubricId] = new Encoder[RubricId] {
  def apply(a: RubricId): Json = Encoder.encodeInt(a.value)
}
{
  "id": 1
}

Custom encoder (generic 1)

import io.circe.Encoder

def anyValEncoder1[A : Encoder, B <: Product with AnyVal]: Encoder[B] = new Encoder[B] {
  def apply(a: B): Json = Encoder.apply[A].apply(a.productElement(0).asInstanceOf[A])
}

implicit val encoderRubricId: Encoder[RubricId] = anyValEncoder1[Int, RubricId]

(-): нужно указывать тип value значения - не удобно поддерживать при изменении сигнатуры AnyVal класса

{
  "id": 1
}

Custom encoder (generic 2 with case class apply hint)

import io.circe.Encoder

def anyValEncoder2[A : Encoder, B <: Product with AnyVal](cons: A => B): Encoder[B] = new Encoder[B] {
  def apply(a: B): Json = Encoder.apply[A].apply(a.productElement(0).asInstanceOf[A])
}

implicit val encoderRubricId: Encoder[RubricId] = anyValEncoder2(RubricId)

(-): немного вводит в заблуждение apply метод у RubricId как cons, но он подсказывает типы

(+): не нужно поддерживание изменение сигнатуры AnyVal класса

{
  "id": 1
}

Custom encoder (generic 3 with case class apply hint and io.circe.syntax asJson) ⭐

import io.circe.Encoder
import io.circe.syntax._

def anyValEncoder3[A : Encoder, B <: Product with AnyVal](cons: A => B): Encoder[B] = new Encoder[B] {
  def apply(a: B): Json = a.productElement(0).asInstanceOf[A].asJson
}

implicit val encoderRubricId: Encoder[RubricId] = anyValEncoder3(RubricId)

(+) выбор пал на этот вариант

{
  "id": 1
}

Encoding AnyVal with Shapeless Lazy

circe/circe#469

https://github.com/circe/circe/pull/661/files

package io.circe.generic.extras.encoding

import io.circe.{Encoder, Json}
import shapeless.{::, Generic, HNil, Lazy}

abstract class ValueClassEncoder[A] extends Encoder[A]
 final object ValueClassEncoder {
  implicit def encodeValueClass[A <: AnyVal, R](
    implicit
    gen:    Lazy[Generic.Aux[A, R :: HNil]],
    encode: Encoder[R]
  ): ValueClassEncoder[A] = new ValueClassEncoder[A] {
    override def apply(a: A): Json =
      encode(gen.value.to(a).head)
  }
}
package io.circe.generic.extras

/**
  * Derive an encoder for a value class.
  */
def deriveValueClassEncoder[A](implicit encode: Lazy[ValueClassEncoder[A]]): Encoder[A] = encode.value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment