代码之家  ›  专栏  ›  技术社区  ›  Larousso

具有隐式解析的无形状pb的自定义类型类

  •  1
  • Larousso  · 技术社区  · 7 年前

    我正在尝试使用shapeless从任意case类读取json。

    从T开始,我有一个FieldType[K1,V1]::FieldType[K2,V2]:。。。使用LabelledEnergic

    然后我想构建一个类型为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
    

    有人能帮我吗?

    谢谢Alex。

    4 回复  |  直到 7 年前
        1
  •  0
  •   Dmytro Mitin    7 年前

      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
  •  0
  •   Dmytro Mitin    7 年前

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

    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
  •  0
  •   Larousso    7 年前

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

    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->读取[T1]::读取[T2]。。。

    2. 序列读取[T1]::读取[T2]=&燃气轮机;读取[T1::T2…]

    但我在第一步失败了。。。

        4
  •  0
  •   Larousso    7 年前

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

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