How to overload class product method

I am trying to implement the ReadJsonCodec class using the mechanism for automatically creating a type class in Shapeless.

Here is my ReadCodecCompanionObject:

object ReadCodec extends LabelledProductTypeClassCompanion[ReadCodec] {
  implicit object StringCodec extends SimpleCodec[String] {
    def read(j: Json): String = j.stringOr(throw ...)
  }
  implicit object IntCodec ...
  implicit object BooleanCodec ...
  implicit object LongCodec ...
  implicit object ShortCodec ...
  implicit object DoubleCodec ...
  implicit object BigDecimalCodec ...

  implicit def readCodecInstance: LabelledProductTypeClass[ReadCodec] = new LabelledProductTypeClass[ReadCodec] {
    def emptyProduct = new ReadCodec[HNil] {
      // This will silently accept extra fields within a JsonObject
      // To change this behavior make sure json is a JsonObject and that it is empty
      def read(json: Json) = HNil
    }

    def product[F, T <: HList](name: String, FHead: ReadCodec[F], FTail: ReadCodec[T]) = new ReadCodec[F :: T] {
      def read(json: Json): F :: T = {
        val map = castOrThrow(json)
        val fieldValue = map.getOrElse(name, throw new MappingException(s"Expected field $name on JsonObject $map"))
        // Try reading the value of the field
        // If we get a mapping exception, intercept it and add the name of this field to the path
        // If we get another exception, don't touch!
        // Pitfall: if handle did not accept a PartialFunction, we could transform an unknow exception into a match exception
        val head: F = Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get
        val tail = FTail.read(json)
        head :: tail
      }
    }

    def product[A, T <: HList](name: String, FHead: ReadCodec[Option[A]], FTail: ReadCodec[T]) = new ReadCodec[Option[A] :: T] {
      def read(json: Json): Option[A] :: T = {
        val map = castOrThrow(json)
        val head: Option[A] = map.get(name).map { fieldValue =>
          Try(FHead.read(fieldValue)).handle{ case MappingException(msg, path) => throw MappingException(msg, s"$name/$path")}.get.get
        }
        val tail = FTail.read(json)
        head :: tail
      }
    }

    def project[F, G](instance: => ReadCodec[G], to : F => G, from : G => F) = new ReadCodec[F] {
      def read(json: Json): F = from(instance.read(json))
    }
  }
}

This is a rather difficult part of the code to just quickly understand, but actually quite simple as soon as you understand it. The important part is two methods def product. The problem I am facing is that I want this codec to accept a Json AST that does not have a field if this field is matched with a type value Option[A]. This means that I need a product function to find out if the HList head is of type Option [A] or not.

Namely:

case class Foo(a: String, b: Option[Boolean])
val test = read[Foo](json"""{"a" : "Beaver"}""") // should work

, b. , .

- , F Option [A]. , , . , Scala , . , . , Scala.

, TypeTag , F Option [_] . , . , TypeTag ( ), , .

- .

+4
1

F. ReadCodec. , , , :

trait ReadCodecAndTypeTag[A] {
  val rc: ReadCodec[A]
  val tt: TypeTag[A]
}

-from-optional-value-in-a-map :

trait OReadCodec[A] {
  val rc: ReadCodec[A]
  def missingField(name: String, map: Any): A =
    throw new MappingException(s"Expected field $name on JsonObject $map")
}
implicit object StringCodec extends OReadCodec[String] {
  val rc = new ReadCodec[String] {...}
}
implicit object IntCodec ...
...
implicit def OptionCodec[A](implicit orc: OReadCodec[A]) =
 new OReadCodec[Option[A]] {
  val rc = ...
  override def missingField(name: String, map: Any) = None
}
...
def product[F, T <: HList](name: String, FHead: OReadCodec[F], FTail: OReadCodec[T]) =
  new OReadCodec[F :: T] {
    val rc = new ReadCodec[F :: T] {
      def read(json: Json): F :: T = {
      val map = castOrThrow(json)
      val fieldValue = map.getOrElse(name, FHead.missingField(name, map))
      val head: F = ...
      ...
    }
  }
}

implicit def ReadCodecFromOReadCodec[A](implicit orc: OReadCodec[A]) = orc.rc
+2

All Articles