我终于以另一种方式成功了:
object rules {
import play.api.libs.json._
import play.api.libs.functional.syntax._
import scala.annotation.implicitNotFound
import shapeless.labelled._
import shapeless.syntax.singleton._
import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}
import shapeless.ops.record.Selector
trait SequenceReads[In <: HList] {
type Out
def apply(in: In): Reads[Out]
}
object SequenceReads {
import play.api.libs.functional.syntax._
type Aux[A <: HList, B <: HList] = SequenceReads[A] {type Out = B}
implicit def sequenceHnil[T, R <: HList, TR <: HList](): Aux[HNil, HNil] = new SequenceReads[HNil] {
type Out = HNil
override def apply(in: HNil): Reads[Out] = {
throw new RuntimeException("Oups")
}
}
implicit def sequenceSingleton[T, K <: Symbol](
implicit witness: Witness.Aux[K]
): Aux[FieldType[K, Reads[T]] :: HNil, T :: HNil] = new SequenceReads[FieldType[K, Reads[T]] :: HNil] {
type Out = T :: HNil
override def apply(in: FieldType[K, Reads[T]] :: HNil): Reads[T :: HNil] = {
val name = witness.value.name
(__ \ name).read(in.head).map(_ :: HNil)
}
}
implicit def sequence[T, K <: Symbol, R <: HList, TR <: HList](
implicit
witness: Witness.Aux[K],
req: Lazy[SequenceReads.Aux[R, TR]]
): Aux[FieldType[K, Reads[T]] :: R, T :: TR] = new SequenceReads[FieldType[K, Reads[T]] :: R] {
type Out = T :: TR
override def apply(in: FieldType[K, Reads[T]] :: R): Reads[Out] = {
val name = witness.value.name
val head: Reads[T] = (__ \ name).read(in.head)
val value: Reads[TR] = req.value.apply(in.tail)
(head and value) {
_ :: _
}
}
}
implicit class SequenceReadsOps[In <: HList](in: In) {
class Builder[Out <: HList] {
def apply(
implicit
sequence: SequenceReads.Aux[In, Out]
): Reads[Out] = {
sequence(in)
}
}
def sequence[R <: HList](implicit s: SequenceReads.Aux[In, R]) = new Builder[R].apply(s)
}
}
@implicitNotFound("Implicit not found: Rules type or fields are not valid")
trait RuleValidation[Repr <: HList, Rules <: HList]
object RuleValidation {
implicit def validateHNil[Repr <: HList] : RuleValidation[Repr, HNil] =
new RuleValidation[Repr, HNil] {}
implicit def validateSingleton[Repr <: HList, K <: Symbol, V] (
implicit
sel: Selector.Aux[Repr, K, V]
): RuleValidation[Repr, FieldType[K, Reads[V]] :: HNil] =
new RuleValidation[Repr, FieldType[K, Reads[V]] :: HNil] {}
implicit def validateHCons[Repr <: HList, H, R <: HList, K <: Symbol, V] (
implicit
sel: Selector.Aux[Repr, K, V],
validation: RuleValidation[Repr, R]
): RuleValidation[Repr, FieldType[K, Reads[V]] :: R] =
new RuleValidation[Repr, FieldType[K, Reads[V]] :: R] {}
}
object ReadsWithRules {
implicit def readsHNil: Reads[HNil] = new Reads[HNil] {
override def reads(json: JsValue): JsResult[HNil] = JsSuccess(HNil)
}
implicit def readHCons[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :: T] =
new Reads[FieldType[K, H] :: T] {
override def reads(json: JsValue): JsResult[FieldType[K, H] :: T] = {
val name = witness.value
val jsonH = (__ \ name).read(readsH)
val jsonT = readsT
(jsonH and jsonT) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
implicit def readsGeneric[Repr, A, R <: HList](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[Reads[Repr]]): Reads[A] = {
readsRepr.value.map(r => gen.from(r))
}
}
trait JsonRead[T]
def jsonRead[T]: JsonRead[T] = new JsonRead[T] {}
implicit class WithRules[A](gen: JsonRead[A]) {
def readsWithRules[R <: HList, K <: Symbol, V, T0 <: HList, ARepr <: HList, AKeys <: HList, RKeys <: HList, Validation <: HList](rules: FieldType[K, V] :: T0)(
implicit
genA: LabelledGeneric.Aux[A, ARepr],
readsInst: Reads[A],
sequenceReads: SequenceReads[FieldType[K, V] :: T0],
validation: RuleValidation[ARepr, FieldType[K, V] :: T0]
): Reads[A] = Reads[A] { json =>
val valueA: JsResult[A] = readsInst.reads(json)
val valueR: JsResult[sequenceReads.Out] = sequenceReads(rules).reads(json)
(valueA, valueR) match {
case (err1: JsError, err2: JsError) => err1 ++ err2
case (err1: JsError, JsSuccess(_, _)) => err1
case (JsSuccess(_, _), err2: JsError) => err2
case (JsSuccess(v, p), _) => JsSuccess(v, p)
}
}
}
}
和测试
object Test extends App {
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import shapeless._
import syntax.singleton._
import rules._
case class Other(name: String)
case class MonPojo(toto: String, tata: Int, other: Other)
object MonPojo {
implicit val readsOther = Json.reads[Other]
implicit val reads: Reads[MonPojo] = Json.reads[MonPojo]
}
val strReads = pattern(".*".r)
private val value: Reads[MonPojo] = jsonRead[MonPojo].readsWithRules(
('tata ->> (min(0) keepAnd max(150))) ::
HNil
)
println(s"!!! ${
value.reads(Json.obj(
"toto" -> "test",
"other" -> Json.obj("name" -> "test"),
"tata" -> 25
))
}")
}