自定义类型 class 带有隐式解析的无形状 pb
custom type class with shapless pb with implicit resolution
我正在尝试使用 shapeless 从任意案例 class 中读取剧本 json。
目前我正在尝试执行以下步骤
从 T,我有一个 FieldType[K1, V1] :: FieldType[K2, V2] :: ... 使用 LabelledGeneric
然后我想构建一个 Reads[V1] :: Reads[V2] 类型的 HList ...
这是我正在使用的代码:
/*
* To build the json reads from T
*/
trait HReads[PRepr <: HList] {
type Out
def reads: Out
}
object HReads {
type Aux[PRepr <: HList, Out1 <: HList] = HReads[PRepr] { type Out = Out1 }
implicit def readsHNil(): Aux[HNil, HNil] = new HReads[HNil] {
type Out = HNil
override def reads: Out = {
throw new RuntimeException("Oups")
}
}
implicit def readsSingleton[T, K <: Symbol](
implicit
kWitness: Witness.Aux[K],
jsReads: play.api.libs.json.Reads[T]
): Aux[FieldType[K, T] :: HNil, Reads[T] :: HNil] = new HReads[FieldType[K, T] :: HNil] {
type Out = Reads[T] :: HNil
override def reads: Out = {
val name: String = kWitness.value.name
val pathReads: Reads[T] = (__ \ name).read[T](jsReads)
pathReads :: HNil
}
}
implicit def readsStd[T, K <: Symbol, RestRepr <: HList, Rest <: HList](
implicit
kWitness: Witness.Aux[K],
jsReads: Reads[T],
hreads: Lazy[HReads.Aux[RestRepr, Rest]]
): Aux[FieldType[K, T] :: RestRepr, Reads[T] :: Rest] = new HReads[FieldType[K, T] :: RestRepr] {
type Out = Reads[T] :: Rest
override def reads: Out = {
val name: String = kWitness.value.name
val pathReads: Reads[T] = (__ \ name).read[T](jsReads)
val value: Rest = hreads.value.reads
pathReads :: value
}
}
def jsonReads[P]: JsonReads[P] = new JsonReads[P] {}
implicit class JsonReadsOps[In](in: JsonReads[In]) {
def jsonReads[K <: Symbol, T, InRepr <: HList, HR <: HList]()(
implicit
gen: LabelledGeneric.Aux[In, FieldType[K, T] :: InRepr],
hreads: HReads.Aux[FieldType[K, T] :: InRepr, Reads[T] :: HR]
): Reads[T] :: HR = {
hreads.reads
}
}
}
// And trying to use this like that :
import HReads._
implicit val l = LabelledGeneric[MonPojo]
private val allReads = jsonReads[MonPojo].jsonReads()
println(s"All Reads $allReads")
//[error] validation\validation.scala:428: could not find implicit value for parameter hreads: validation.validations.HReads.Aux[shapeless.labelled.FieldType[K,T] :: InRepr,play.api.libs.json.Reads[T] :: HR]
//[error] private val allReads = jsonReads[MonPojo].jsonReads()
//[error] ^
//[error] one error found
有人可以帮助我吗?
谢谢亚历克斯。
Then I want to build an HList of type Reads[V1] :: Reads[V2] ...
不清楚为什么你需要一个 HList of Reads 而不是 Reads of HList(所以我猜你不需要另一种类型 class HReads,Reads 应该足够了)。我猜你需要实现隐式:
implicit val readsHNil: Reads[HNil] = ???
implicit def readHCons[K <: Symbol, H, T <: HList](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :: T] = ???
implicit def readsGeneric[Repr, A](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[Reads[Repr]]): Reads[A] = ???
如果需要,还有类似的两个副产品。
我写了一些实现
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil, Inl, LabelledGeneric, Lazy, Witness}
import shapeless.labelled.FieldType
import play.api.libs.json._
import shapeless.syntax.singleton._
implicit val readsHNil: Reads[HNil] = Reads {
case JsArray(values) if values.isEmpty => JsSuccess(HNil)
case JsObject(values) if values.isEmpty => JsSuccess(HNil)
case _ => JsError()
}
private def listToJsResult[K <: Symbol, H, T <: HList](l: List[JsValue])(implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): JsResult[FieldType[K, H] :: T] = {
val name = witness.value
l match {
case Nil => JsError()
case scala.::(head, tail) => for {
h <- readsH.reads(head)
t <- /*listToJsResult[K1, H1, T1](tail)*/ readsT.reads(JsArray(tail))
} yield (name ->> h).asInstanceOf[FieldType[K, H]] :: t
}
}
implicit val readsCNil: Reads[CNil] = Reads(_ => throw new Exception)
implicit def readHCons[K <: Symbol, H, T <: HList](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :: T] =
Reads {
case arr: JsArray => listToJsResult[K, H, T](arr.value.toList)
case obj: JsObject => listToJsResult[K, H, T](obj.values.toList)
case js => listToJsResult[K, H, T](List(js))
}
implicit def readCCons[K <: Symbol, H, T <: Coproduct](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :+: T] = {
val name = witness.value
Reads { json =>
(for {
h <- readsH.reads(json)
} yield Inl(name ->> h).asInstanceOf[FieldType[K, H] :+: T]) orElse {
for {
t <- readsT.reads(json)
} yield Inr(name ->> t).asInstanceOf[FieldType[K, H] :+: T]
}
}
}
implicit def readsGeneric[Repr, A](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[Reads[Repr]]): Reads[A] =
Reads(json => readsRepr.value.reads(json).map(gen.from))
def reads[A](json: JsValue)(implicit readsInst: Reads[A]): JsResult[A] = readsInst.reads(json)
但它们似乎工作不正常
它们似乎工作正常:
sealed trait MyTrait
case class MyClass1(x: Int, y: Int, z: Int) extends MyTrait
case class MyClass2(x: Int, y: Int) extends MyTrait
reads[MyClass1](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2), "z" -> JsNumber(3))))
//JsSuccess(MyClass1(1,2,3),)
reads[MyTrait](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2), "z" -> JsNumber(3))))
//JsSuccess(MyClass1(1,2,3),)
reads[MyTrait](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2))))
//JsSuccess(MyClass2(1,2),)
答案部分基于库 shapelaysson。
我可以让你的代码像这样工作
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).reads(json)
val jsonT = readsT.reads(json)
(jsonH and jsonT) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T])
}
}
但我的最终目标是能够添加额外的规则:比如
import validation2.ReadsWithRules._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
val r: Reads[MonPojo] = LabelledGeneric[MonPojo].readsWithRules(('numericField ->> (min(0) keepAnd max(150))) :: HNil)
r.reads(Json.obj(
"stringField" -> "Tata",
"numericField" -> 42
))
println(s"All Reads $r")
我试着用这个
调整你的代码
trait ReadsWithRules[T, R <: HList] {
def withRules(rules: R): Reads[T]
}
trait ReadsWithRulesLowerPriority {
implicit def readsHNil[R <: HNil]: ReadsWithRules[HNil, R] = new ReadsWithRules[HNil, R] {
def withRules(rules: R) =
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: ReadsWithRules[T, R]): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R) = 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.withRules(rules)
(jsonH and jsonT) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
}
object ReadsWithRules extends ReadsWithRulesLowerPriority {
implicit def readHConsWithRule[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
at: shapeless.ops.record.Selector.Aux[R, K, Reads[H]],
w: <:<[H, JsValue],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R) = new Reads[FieldType[K, H] :: T] {
override def reads(json: JsValue): JsResult[FieldType[K, H] :: T] = {
val name = witness.value
val additionnalRule: Reads[H] = at(rules)
val jsonH = (__ \ name).read(readsH).andThen(additionnalRule)
val jsonT = readsT
(jsonH and jsonT.withRules(rules)) ((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[ReadsWithRules[Repr, R]]): ReadsWithRules[A, R] =
new ReadsWithRules[A, R] {
override def withRules(rules: R) : Reads[A] = {
readsRepr.value.withRules(rules).map(r => gen.from(r))
}
}
implicit class WithRules[T](gen: LabelledGeneric[T]) {
def readsWithRules[R <: HList](rules: R)(implicit readWithRule: ReadsWithRules[T, R]): Reads[T] = {
readWithRule.withRules(rules)
}
}
}
但是我有一个隐式解析错误。
我之前的想法是分解子步骤中的问题:
第一步 T -> 读数[T1] :: 读数[T2] ...
合并 Reads[T1] 中的附加规则 :: Reads[T2] ...
序列读数[T1] :: 读数[T2] ... => 读数[T1 :: T2 ...]
但是我在第 1 步失败了...
这里是 ReadsWithRules 的实现:
trait ReadsWithRules[T, R <: HList] {
def withRules(rules: R): Reads[T]
}
trait ReadsWithRulesLowerPriority {
implicit def readsNoRule[T](implicit reads: Reads[T]): ReadsWithRules[T, HNil] = new ReadsWithRules[T, HNil] {
override def withRules(rules: HNil): Reads[T] = reads
}
implicit def readsGeneric[Repr, A, R <: HList](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[ReadsWithRules[Repr, R]]
): ReadsWithRules[A, R] =
new ReadsWithRules[A, R] {
override def withRules(rules: R): Reads[A] = {
readsRepr.value.withRules(rules).map(r => gen.from(r))
}
}
}
object ReadsWithRules extends ReadsWithRulesLowerPriority {
implicit def readHNil[R <: HList]: ReadsWithRules[HNil, R] = new ReadsWithRules[HNil, R] {
override def withRules(rules: R): Reads[HNil] = implicitly[Reads[HNil]]
}
implicit def readNoRuleForHead[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
noRule: LacksKey[R, K],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]
): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R): 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 rH = (__ \ name).read(readsH)
(rH and readsT.withRules(rules)) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
implicit def readRuleForHead[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
at: shapeless.ops.record.Selector.Aux[R, K, Reads[H]],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]
): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R): 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 additionalRule: Reads[H] = at(rules)
val rH = (__ \ name).read(readsH) andKeep (__ \ name).read(additionalRule)
(rH and readsT.withRules(rules)) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
}
def readsWithRules[T, R <: HList](rules: R)(implicit readWithRule: ReadsWithRules[T, R]): Reads[T] =
readWithRule.withRules(rules)
case class MonPojo(numericField: Int)
val r: Reads[MonPojo] =
readsWithRules[MonPojo, FieldType[Symbol with Tagged["numericField"], Reads[Int]] :: HNil](
('numericField ->> (min(0) keepAnd max(150))) :: HNil
)
println(
r.reads(Json.obj(
"stringField" -> "Tata",
"numericField" -> 42
))
)
//JsSuccess(MonPojo(42),)
我终于通过另一种方式成功了:
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
))
}")
}
感谢您的帮助,我会尝试您的解决方案。
我正在尝试使用 shapeless 从任意案例 class 中读取剧本 json。
目前我正在尝试执行以下步骤
从 T,我有一个 FieldType[K1, V1] :: FieldType[K2, V2] :: ... 使用 LabelledGeneric
然后我想构建一个 Reads[V1] :: Reads[V2] 类型的 HList ...
这是我正在使用的代码:
/*
* To build the json reads from T
*/
trait HReads[PRepr <: HList] {
type Out
def reads: Out
}
object HReads {
type Aux[PRepr <: HList, Out1 <: HList] = HReads[PRepr] { type Out = Out1 }
implicit def readsHNil(): Aux[HNil, HNil] = new HReads[HNil] {
type Out = HNil
override def reads: Out = {
throw new RuntimeException("Oups")
}
}
implicit def readsSingleton[T, K <: Symbol](
implicit
kWitness: Witness.Aux[K],
jsReads: play.api.libs.json.Reads[T]
): Aux[FieldType[K, T] :: HNil, Reads[T] :: HNil] = new HReads[FieldType[K, T] :: HNil] {
type Out = Reads[T] :: HNil
override def reads: Out = {
val name: String = kWitness.value.name
val pathReads: Reads[T] = (__ \ name).read[T](jsReads)
pathReads :: HNil
}
}
implicit def readsStd[T, K <: Symbol, RestRepr <: HList, Rest <: HList](
implicit
kWitness: Witness.Aux[K],
jsReads: Reads[T],
hreads: Lazy[HReads.Aux[RestRepr, Rest]]
): Aux[FieldType[K, T] :: RestRepr, Reads[T] :: Rest] = new HReads[FieldType[K, T] :: RestRepr] {
type Out = Reads[T] :: Rest
override def reads: Out = {
val name: String = kWitness.value.name
val pathReads: Reads[T] = (__ \ name).read[T](jsReads)
val value: Rest = hreads.value.reads
pathReads :: value
}
}
def jsonReads[P]: JsonReads[P] = new JsonReads[P] {}
implicit class JsonReadsOps[In](in: JsonReads[In]) {
def jsonReads[K <: Symbol, T, InRepr <: HList, HR <: HList]()(
implicit
gen: LabelledGeneric.Aux[In, FieldType[K, T] :: InRepr],
hreads: HReads.Aux[FieldType[K, T] :: InRepr, Reads[T] :: HR]
): Reads[T] :: HR = {
hreads.reads
}
}
}
// And trying to use this like that :
import HReads._
implicit val l = LabelledGeneric[MonPojo]
private val allReads = jsonReads[MonPojo].jsonReads()
println(s"All Reads $allReads")
//[error] validation\validation.scala:428: could not find implicit value for parameter hreads: validation.validations.HReads.Aux[shapeless.labelled.FieldType[K,T] :: InRepr,play.api.libs.json.Reads[T] :: HR]
//[error] private val allReads = jsonReads[MonPojo].jsonReads()
//[error] ^
//[error] one error found
有人可以帮助我吗?
谢谢亚历克斯。
Then I want to build an HList of type Reads[V1] :: Reads[V2] ...
不清楚为什么你需要一个 HList of Reads 而不是 Reads of HList(所以我猜你不需要另一种类型 class HReads,Reads 应该足够了)。我猜你需要实现隐式:
implicit val readsHNil: Reads[HNil] = ???
implicit def readHCons[K <: Symbol, H, T <: HList](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :: T] = ???
implicit def readsGeneric[Repr, A](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[Reads[Repr]]): Reads[A] = ???
如果需要,还有类似的两个副产品。
我写了一些实现
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil, Inl, LabelledGeneric, Lazy, Witness}
import shapeless.labelled.FieldType
import play.api.libs.json._
import shapeless.syntax.singleton._
implicit val readsHNil: Reads[HNil] = Reads {
case JsArray(values) if values.isEmpty => JsSuccess(HNil)
case JsObject(values) if values.isEmpty => JsSuccess(HNil)
case _ => JsError()
}
private def listToJsResult[K <: Symbol, H, T <: HList](l: List[JsValue])(implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): JsResult[FieldType[K, H] :: T] = {
val name = witness.value
l match {
case Nil => JsError()
case scala.::(head, tail) => for {
h <- readsH.reads(head)
t <- /*listToJsResult[K1, H1, T1](tail)*/ readsT.reads(JsArray(tail))
} yield (name ->> h).asInstanceOf[FieldType[K, H]] :: t
}
}
implicit val readsCNil: Reads[CNil] = Reads(_ => throw new Exception)
implicit def readHCons[K <: Symbol, H, T <: HList](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :: T] =
Reads {
case arr: JsArray => listToJsResult[K, H, T](arr.value.toList)
case obj: JsObject => listToJsResult[K, H, T](obj.values.toList)
case js => listToJsResult[K, H, T](List(js))
}
implicit def readCCons[K <: Symbol, H, T <: Coproduct](implicit
witness: Witness.Aux[K],
readsH: Reads[H],
readsT: Reads[T]): Reads[FieldType[K, H] :+: T] = {
val name = witness.value
Reads { json =>
(for {
h <- readsH.reads(json)
} yield Inl(name ->> h).asInstanceOf[FieldType[K, H] :+: T]) orElse {
for {
t <- readsT.reads(json)
} yield Inr(name ->> t).asInstanceOf[FieldType[K, H] :+: T]
}
}
}
implicit def readsGeneric[Repr, A](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[Reads[Repr]]): Reads[A] =
Reads(json => readsRepr.value.reads(json).map(gen.from))
def reads[A](json: JsValue)(implicit readsInst: Reads[A]): JsResult[A] = readsInst.reads(json)
但它们似乎工作不正常
它们似乎工作正常:
sealed trait MyTrait
case class MyClass1(x: Int, y: Int, z: Int) extends MyTrait
case class MyClass2(x: Int, y: Int) extends MyTrait
reads[MyClass1](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2), "z" -> JsNumber(3))))
//JsSuccess(MyClass1(1,2,3),)
reads[MyTrait](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2), "z" -> JsNumber(3))))
//JsSuccess(MyClass1(1,2,3),)
reads[MyTrait](JsObject(Seq("x" -> JsNumber(1), "y" -> JsNumber(2))))
//JsSuccess(MyClass2(1,2),)
答案部分基于库 shapelaysson。
我可以让你的代码像这样工作
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).reads(json)
val jsonT = readsT.reads(json)
(jsonH and jsonT) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T])
}
}
但我的最终目标是能够添加额外的规则:比如
import validation2.ReadsWithRules._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
val r: Reads[MonPojo] = LabelledGeneric[MonPojo].readsWithRules(('numericField ->> (min(0) keepAnd max(150))) :: HNil)
r.reads(Json.obj(
"stringField" -> "Tata",
"numericField" -> 42
))
println(s"All Reads $r")
我试着用这个
调整你的代码 trait ReadsWithRules[T, R <: HList] {
def withRules(rules: R): Reads[T]
}
trait ReadsWithRulesLowerPriority {
implicit def readsHNil[R <: HNil]: ReadsWithRules[HNil, R] = new ReadsWithRules[HNil, R] {
def withRules(rules: R) =
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: ReadsWithRules[T, R]): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R) = 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.withRules(rules)
(jsonH and jsonT) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
}
object ReadsWithRules extends ReadsWithRulesLowerPriority {
implicit def readHConsWithRule[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
at: shapeless.ops.record.Selector.Aux[R, K, Reads[H]],
w: <:<[H, JsValue],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R) = new Reads[FieldType[K, H] :: T] {
override def reads(json: JsValue): JsResult[FieldType[K, H] :: T] = {
val name = witness.value
val additionnalRule: Reads[H] = at(rules)
val jsonH = (__ \ name).read(readsH).andThen(additionnalRule)
val jsonT = readsT
(jsonH and jsonT.withRules(rules)) ((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[ReadsWithRules[Repr, R]]): ReadsWithRules[A, R] =
new ReadsWithRules[A, R] {
override def withRules(rules: R) : Reads[A] = {
readsRepr.value.withRules(rules).map(r => gen.from(r))
}
}
implicit class WithRules[T](gen: LabelledGeneric[T]) {
def readsWithRules[R <: HList](rules: R)(implicit readWithRule: ReadsWithRules[T, R]): Reads[T] = {
readWithRule.withRules(rules)
}
}
}
但是我有一个隐式解析错误。
我之前的想法是分解子步骤中的问题:
第一步 T -> 读数[T1] :: 读数[T2] ...
合并 Reads[T1] 中的附加规则 :: Reads[T2] ...
序列读数[T1] :: 读数[T2] ... => 读数[T1 :: T2 ...]
但是我在第 1 步失败了...
这里是 ReadsWithRules 的实现:
trait ReadsWithRules[T, R <: HList] {
def withRules(rules: R): Reads[T]
}
trait ReadsWithRulesLowerPriority {
implicit def readsNoRule[T](implicit reads: Reads[T]): ReadsWithRules[T, HNil] = new ReadsWithRules[T, HNil] {
override def withRules(rules: HNil): Reads[T] = reads
}
implicit def readsGeneric[Repr, A, R <: HList](implicit
gen: LabelledGeneric.Aux[A, Repr],
readsRepr: Lazy[ReadsWithRules[Repr, R]]
): ReadsWithRules[A, R] =
new ReadsWithRules[A, R] {
override def withRules(rules: R): Reads[A] = {
readsRepr.value.withRules(rules).map(r => gen.from(r))
}
}
}
object ReadsWithRules extends ReadsWithRulesLowerPriority {
implicit def readHNil[R <: HList]: ReadsWithRules[HNil, R] = new ReadsWithRules[HNil, R] {
override def withRules(rules: R): Reads[HNil] = implicitly[Reads[HNil]]
}
implicit def readNoRuleForHead[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
noRule: LacksKey[R, K],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]
): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R): 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 rH = (__ \ name).read(readsH)
(rH and readsT.withRules(rules)) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
implicit def readRuleForHead[K <: Symbol, H, T <: HList, R <: HList](implicit
witness: Witness.Aux[K],
at: shapeless.ops.record.Selector.Aux[R, K, Reads[H]],
readsH: Reads[H],
readsT: ReadsWithRules[T, R]
): ReadsWithRules[FieldType[K, H] :: T, R] =
new ReadsWithRules[FieldType[K, H] :: T, R] {
override def withRules(rules: R): 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 additionalRule: Reads[H] = at(rules)
val rH = (__ \ name).read(readsH) andKeep (__ \ name).read(additionalRule)
(rH and readsT.withRules(rules)) ((a, b) => (name ->> a :: b).asInstanceOf[FieldType[K, H] :: T]).reads(json)
}
}
}
}
def readsWithRules[T, R <: HList](rules: R)(implicit readWithRule: ReadsWithRules[T, R]): Reads[T] =
readWithRule.withRules(rules)
case class MonPojo(numericField: Int)
val r: Reads[MonPojo] =
readsWithRules[MonPojo, FieldType[Symbol with Tagged["numericField"], Reads[Int]] :: HNil](
('numericField ->> (min(0) keepAnd max(150))) :: HNil
)
println(
r.reads(Json.obj(
"stringField" -> "Tata",
"numericField" -> 42
))
)
//JsSuccess(MonPojo(42),)
我终于通过另一种方式成功了:
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
))
}")
}
感谢您的帮助,我会尝试您的解决方案。