鉴于以下优秀shapeless-guide:
package net
import shapeless.labelled.FieldType
import shapeless._
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
本书介绍了如何编写通用JsonEncoder
,即给定A
,提供A
的JSON表示:
trait JsonEncoder[A] {
def encode(value: A): JsonValue
}
object JsonEncoder {
def apply[A](implicit ev: JsonEncoder[A]): JsonEncoder[A] =
ev
def instance[A](f: A => JsonValue): JsonEncoder[A] =
new JsonEncoder[A] {
override def encode(x: A): JsonValue = f(x)
}
implicit val doubleEncoder: JsonEncoder[Double] =
instance[Double](JsonNumber)
implicit val stringEncoder: JsonEncoder[String] =
instance[String](JsonString)
implicit val boolEncoder: JsonEncoder[Boolean] =
instance[Boolean](JsonBoolean)
implicit def optionEncoder[A](implicit ev: JsonEncoder[A]): JsonEncoder[Option[A]] =
instance[Option[A]] {
case Some(a) => ev.encode(a)
case None => JsonNull
}
implicit def listEncoder[A](implicit ev: JsonEncoder[A]): JsonEncoder[List[A]] =
instance[List[A]]( list => JsonArray(list.map(ev.encode)))
trait JsonObjectEncoder[A] extends JsonEncoder[A] {
def encode(value: A): JsonObject
}
def createObjectEncoder[A](fn: A => JsonObject): JsonObjectEncoder[A] =
new JsonObjectEncoder[A] {
override def encode(value: A): JsonObject = fn(value)
}
implicit val hnilEncoder: JsonObjectEncoder[HNil] =
createObjectEncoder(hnil => JsonObject(Nil))
implicit def hlistObjectEncoder[K <: Symbol, H, T <: HList](
implicit
witness: Witness.Aux[K],
hEncoder: Lazy[JsonEncoder[H]],
tEncoder: JsonObjectEncoder[T]
): JsonObjectEncoder[FieldType[K, H] :: T] =
createObjectEncoder { hlist =>
val fieldName = witness.value.name
val head = hEncoder.value.encode(hlist.head)
val tail = tEncoder.encode(hlist.tail)
JsonObject( (fieldName, head) :: tail.fields )
}
implicit def genericObjectEncoder[A, H <: HList](
implicit
generic: LabelledGeneric.Aux[A, H],
hEncoder: Lazy[JsonObjectEncoder[H]]
): JsonEncoder[A] =
createObjectEncoder { value =>
hEncoder.value.encode( generic.to(value) )
}
据我了解,上述代码一般会派生case class
JsonEncoder
。但是,它不会处理sealed trait
或Coproduct:
import net.JsonEncoder
case class A(name: String, age: Double)
scala> JsonEncoder[A]
res0: net.JsonEncoder[A] = net.JsonEncoder$$anon$1@6b974139
sealed trait Parent
case class Kid() extends Parent
scala> JsonEncoder[Parent]
<console>:14: error: could not find implicit value for parameter ev: net.JsonEncoder[Parent]
JsonEncoder[Parent]
^
import shapeless._
type Co = Kid :+: CNil
scala> JsonEncoder[Co]
<console>:20: error: could not find implicit value for parameter ev: net.JsonEncoder[Co]
JsonEncoder[Co]
^
然后,根据我的理解,该文本提供了以下代码,通常为JsonEncoder
派生JsonEncoder
个实例:
implicit val cnilObjectEncoder: JsonObjectEncoder[CNil] =
createObjectEncoder(cnil => throw new Exception("Inconceivable!"))
implicit def coproductObjectEncoder[K <: Symbol, H, T <: Coproduct](
implicit
witness: Witness.Aux[K],
hEncoder: Lazy[JsonEncoder[H]],
tEncoder: JsonObjectEncoder[T]
): JsonObjectEncoder[FieldType[K, H] :+: T] = {
val typeName = witness.value.name
createObjectEncoder {
case Inl(h) =>
JsonObject(List(typeName -> hEncoder.value.encode(h)))
case Inr(t) =>
tEncoder.encode(t)
}
}
然而,从REPL,我仍然无法派生sealed trait
或Coproduct:
import net.JsonEncoder
scala> sealed trait Parent
defined trait Parent
case class Kid() extends Parent
scala> JsonEncoder[Parent]
<console>:14: error: could not find implicit value for parameter ev: net.JsonEncoder[Parent]
JsonEncoder[Parent]
^
import shapeless._
type Co = Kid :+: CNil
scala> JsonEncoder[Co]
<console>:17: error: could not find implicit value for parameter ev: net.JsonEncoder[Co]
JsonEncoder[Co]
^
为什么这两个实例,即Parent
和Co
未派生?
答案 0 :(得分:3)
您应该删除H
的类型参数genericObjectEncoder
的上限。如果您将其限制为HList
的子类型,则排除实际为Coproduct
的情况。
implicit def genericObjectEncoder[A, H](
implicit
generic: LabelledGeneric.Aux[A, H],
hEncoder: Lazy[JsonObjectEncoder[H]]
): JsonEncoder[A] =
createObjectEncoder { value =>
hEncoder.value.encode( generic.to(value) )
}
现在你可以看到它有效。
scala> sealed trait Parent; case class Kid() extends Parent
defined trait Parent
defined class Kid
scala> JsonEncoder[Parent]
res0: JsonEncoder[Parent] = JsonEncoder$$anon$1@127aef0a