Skip to content

Instantly share code, notes, and snippets.

@namiazad
Last active September 26, 2015 23:31
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save namiazad/701d859e1a54d6ccb9a4 to your computer and use it in GitHub Desktop.
Simple serialisation, de-serialsation using Shapeless
package derivation
import java.util.Locale
import shapeless._
import scala.reflect.ClassTag
import scala.util.Try
sealed trait Utensil
case class Plate(capacity: Int, bowl: Boolean) extends Utensil
case class Fork(material: String) extends Utensil
case class Spoon(material: String) extends Utensil
sealed trait Cars
case class Volvo(model: String, isDiesel: Boolean, gearType: Int) extends Cars
case class Benz(model: String, isDiesel: Boolean, gearType: Int) extends Cars
case class BMW(model: String, isDiesel: Boolean, gearType: Int) extends Cars
trait Serializer[T] {
def serialize(t: T): String
}
object Consts {
val TypeIdentifier = "Type: "
}
import Consts._
object Serializer {
implicit val intSerializer: Serializer[Int] = new Serializer[Int] {
def serialize(t: Int): String = t.toString
}
implicit val stringSerializer: Serializer[String] = new Serializer[String] {
def serialize(t: String): String = s""""$t""""
}
implicit val booleanSerializer: Serializer[Boolean] = new Serializer[Boolean] {
def serialize(t: Boolean): String = if (t) "yes" else "no"
}
implicit val HNilSerializer: Serializer[HNil] = new Serializer[HNil] {
def serialize(t: HNil): String = ""
}
implicit def HConsSerializer[H, T <: HList](implicit serH: Lazy[Serializer[H]],
serT: Lazy[Serializer[T]]): Serializer[H :: T] = {
new Serializer[H :: T] {
def serialize(value: H :: T): String = value match {
case (h :: HNil) => serH.value.serialize(h)
case (h :: t) => s"${serH.value.serialize(h)} , ${serT.value.serialize(t)}"
}
}
}
implicit def GenericSerializer[T, R](implicit gen: Generic.Aux[T, R],
serializer: Lazy[Serializer[R]]): Serializer[T] = {
new Serializer[T] {
def serialize(t: T): String = {
val str = serializer.value.serialize(gen.to(t))
if (!str.startsWith(TypeIdentifier)) s"$TypeIdentifier${t.getClass.getName} , $str" else str
}
}
}
//co-product
implicit def CNilSerializer: Serializer[CNil] = new Serializer[CNil] {
def serialize(t: CNil): String = ???
}
implicit def CConsSerializer[H, T <: Coproduct](implicit serH: Lazy[Serializer[H]],
serT: Lazy[Serializer[T]]): Serializer[H :+: T] = {
new Serializer[H :+: T] {
def serialize(c: H :+: T): String = c match {
case Inl(h) => serH.value.serialize(h)
case Inr(t) => serT.value.serialize(t)
}
}
}
}
trait Deserializer[T] {
def deserialize(str: String): Option[T]
}
object Deserializer {
implicit val intDeserializer: Deserializer[Int] = new Deserializer[Int] {
def deserialize(str: String): Option[Int] = Try(str.toInt).toOption
}
implicit val stringDeserializer: Deserializer[String] = new Deserializer[String] {
def deserialize(str: String): Option[String] = Try {
require(str.head == '"' && str.last == '"')
str.substring(1, str.length - 1)
}.toOption
}
implicit val booleanDeserializer: Deserializer[Boolean] = new Deserializer[Boolean] {
def deserialize(str: String): Option[Boolean] = str.toLowerCase(Locale.US) match {
case "yes" => Some(true)
case "no" => Some(false)
case x => None
}
}
implicit val typeDeserializer: Deserializer[Class[_]] = new Deserializer[Class[_]] {
def deserialize(str: String): Option[Class[_]] = Try(Class.forName(str.replace(TypeIdentifier, "").trim)).toOption
}
implicit val HNilDeserializer: Deserializer[HNil] = new Deserializer[HNil] {
def deserialize(str: String): Option[HNil] = Some(HNil)
}
implicit def HConstDeserializer[H, T <: HList](implicit deserH: Deserializer[H],
deserT: Deserializer[T]): Deserializer[H :: T] = new Deserializer[H :: T] {
override def deserialize(str: String): Option[H :: T] = {
val (head, tail) = {
val rest = if (str.startsWith(TypeIdentifier)) str.dropWhile(_ != ',').drop(1).trim else str
val firstComma = rest.indexOf(",")
if (firstComma < 0) (rest, "") else rest.splitAt(firstComma)
}
deserH.deserialize(head.trim) match {
case Some(headEntity) => deserT.deserialize(tail.drop(2).trim) match {
case Some(tailEntity) => Some(headEntity :: tailEntity)
case None => None
}
case None => None
}
}
}
implicit def GenericDeserializer[T, R](implicit gen: Generic.Aux[T, R],
deserializer: Lazy[Deserializer[R]]): Deserializer[T] = {
new Deserializer[T] {
def deserialize(str: String): Option[T] = {
deserializer.value.deserialize(str).map(gen.from)
}
}
}
//co-product
implicit def CNilDeserializer: Deserializer[CNil] = new Deserializer[CNil] {
def deserialize(str: String): Option[CNil] = None
}
implicit def CConsSerializer[H, T <: Coproduct](implicit serH: Lazy[Deserializer[H]],
tag: ClassTag[H],
serT: Lazy[Deserializer[T]]): Deserializer[H :+: T] = {
new Deserializer[H :+: T] {
def deserialize(str: String): Option[H :+: T] = {
val derivedType = str.substring(0, str.indexOf(',')).replace(TypeIdentifier, "").trim
if (derivedType == tag.runtimeClass.getName) {
serH.value.deserialize(str).map(Inl[H, T])
} else {
serT.value.deserialize(str).map(Inr[H, T])
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment