【问题标题】:custom type class with shapless pb with implicit resolution具有隐式分辨率的无形 pb 的自定义类型类
【发布时间】:2017-08-29 17:56:06
【问题描述】:

我正在尝试使用 shapeless 从任意案例类中读取 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

有人可以帮助我吗?

谢谢亚历克斯。

【问题讨论】:

    标签: scala shapeless play-json


    【解决方案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),)
    

    【讨论】:

      【解决方案2】:

      然后我想构建一个 Reads[V1] :: Reads[V2] 类型的 HList ...

      不清楚为什么需要读取的 HList 而不是 HList 的读取(所以我猜您不需要另一个类型类 HRead,读取应该就足够了)。我猜你需要实现隐式:

      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

      【讨论】:

      • 我刚刚发布了答案,请参阅上面的回复
      【解决方案3】:

      我可以让你的代码像这样工作

      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)
            }
          }
        }
      

      但我有一个隐含的解析错误。

      我之前的想法是分解子步骤中的问题:

      1. 第一步 T -> Reads[T1] :: Reads[T2] ...

      2. 在 Reads[T1] :: Reads[T2] 中合并附加规则 ...

      3. 序列读取[T1] ::读取[T2] ... =>读取[T1 :: T2 ...]

      但我在第 1 步失败了......

      【讨论】:

        【解决方案4】:

        我终于以另一种方式成功了:

        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
            ))
          }")
        
        }
        

        感谢您的帮助,我会尝试您的解决方案。

        【讨论】:

        • 亲爱的@Larousso,太好了,您终于通过排序实现了您最初的想法。不客气,和你交流很愉快。感谢您接受我的回答。
        猜你喜欢
        • 2014-06-02
        • 2014-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-26
        • 2020-10-03
        相关资源
        最近更新 更多