Last active
December 3, 2020 13:51
-
-
Save heyrutvik/d5e1162fb8888b4fffdd9110c35d2236 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example | |
import shapeless._ | |
import shapeless.labelled.FieldType | |
object LabelledDerivation extends App { | |
import JsonObjectEncoder._ | |
println { | |
JsonEncoder[IceCream].encode(IceCream("Sunday", 25)) | |
// JsonObject(List((name,JsonString(Sunday)), (price,JsonNumber(25.0)))) | |
} | |
println { | |
JsonEncoder[Employee].encode(Employee("A", 31, manager = true)) | |
// JsonObject(List((name,JsonString(A)), (age,JsonNumber(31.0)), (manager,JsonBoolean(true)))) | |
} | |
println { | |
List( | |
Rectangle(2.0, 1.0), | |
Circle(9.0) | |
).map(JsonEncoder[Shape].encode).mkString("\n") | |
// JsonObject(List((Rectangle,JsonObject(List((width,JsonNumber(2.0)), (height,JsonNumber(1.0))))))) | |
// JsonObject(List((Circle,JsonObject(List((radius,JsonNumber(9.0))))))) | |
} | |
} | |
case class Employee(name: String, age: Int, manager: Boolean) | |
case class IceCream(name: String, price: Int) | |
sealed trait Shape | |
final case class Rectangle(width: Double, height: Double) extends Shape | |
final case class Circle(radius: Double) extends Shape | |
sealed trait JsonValue | |
case class JsonObject(fields: List[(String, JsonValue)]) extends JsonValue | |
case class JsonArray(items: List[JsonValue]) extends JsonValue | |
case class JsonString(value: String) extends JsonValue | |
case class JsonNumber(value: Double) extends JsonValue | |
case class JsonBoolean(value: Boolean) extends JsonValue | |
case object JsonNull extends JsonValue | |
trait JsonEncoder[A] { | |
def encode(value: A): JsonValue | |
} | |
object JsonEncoder { | |
def apply[A](implicit enc: JsonEncoder[A]): JsonEncoder[A] = enc | |
def createEncoder[A](func: A => JsonValue): JsonEncoder[A] = new JsonEncoder[A] { | |
def encode(value: A): JsonValue = func(value) | |
} | |
implicit val stringEncoder: JsonEncoder[String] = createEncoder(string => JsonString(string)) | |
implicit val intEncoder: JsonEncoder[Int] = createEncoder(int => JsonNumber(int.toDouble)) | |
implicit val doubleEncoder: JsonEncoder[Double] = createEncoder(double => JsonNumber(double)) | |
implicit val booleanEncoder: JsonEncoder[Boolean] = createEncoder(boolean => JsonBoolean(boolean)) | |
implicit def listEncoder[A](implicit enc: JsonEncoder[A]): JsonEncoder[List[A]] = | |
createEncoder(list => JsonArray(list.map(enc.encode))) | |
implicit def optionEncoder[A](implicit enc: JsonEncoder[A]): JsonEncoder[Option[A]] = | |
createEncoder(opt => opt.map(enc.encode).getOrElse(JsonNull)) | |
} | |
trait JsonObjectEncoder[A] extends JsonEncoder[A] { | |
def encode(value: A): JsonObject | |
} | |
object JsonObjectEncoder { | |
def createObjectEncoder[A](func: A => JsonObject): JsonObjectEncoder[A] = new JsonObjectEncoder[A] { | |
def encode(value: A): JsonObject = func(value) | |
} | |
implicit val hnilEncoder: JsonObjectEncoder[HNil] = | |
createObjectEncoder(_ => JsonObject(Nil)) | |
implicit def hlistEncoder[K <: Symbol, H, T <: HList](implicit | |
witness: Witness.Aux[K], | |
hEncoder: JsonEncoder[H], | |
tEncoder: JsonObjectEncoder[T] | |
): JsonObjectEncoder[FieldType[K, H] :: T] = { | |
val fieldName = witness.value.name | |
createObjectEncoder { hlist => | |
val head = hEncoder.encode(hlist.head) | |
val tail = tEncoder.encode(hlist.tail) | |
JsonObject((fieldName, head) :: tail.fields) | |
} | |
} | |
implicit val cnilEncoder: JsonObjectEncoder[CNil] = | |
createObjectEncoder(_ => throw new Exception("Inconceivable!")) | |
implicit def coproductEncoder[K <: Symbol, H, T <: Coproduct](implicit | |
witness: Witness.Aux[K], | |
hEncoder: JsonEncoder[H], | |
tEncoder: JsonObjectEncoder[T] | |
): JsonObjectEncoder[FieldType[K, H] :+: T] = { | |
val typeName = witness.value.name | |
createObjectEncoder { | |
case Inl(h) => JsonObject(List(typeName -> hEncoder.encode(h))) | |
case Inr(t) => tEncoder.encode(t) | |
} | |
} | |
implicit def genericObjectEncoder[A, H](implicit | |
gen: LabelledGeneric.Aux[A, H], | |
hEncoder: JsonObjectEncoder[H] | |
): JsonEncoder[A] = | |
createObjectEncoder { value => | |
hEncoder.encode(gen.to(value)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment