Shapeless code to convert Map[String, Any] to case class cannot handle optional substructures

后端 未结 1 1524
死守一世寂寞
死守一世寂寞 2021-01-24 06:03

I\'m trying to use this https://stackoverflow.com/a/31641779/1586965 (How to use shapeless to convert generic Map[String, Any] to case class inside generic function?) to process

1条回答
  •  一整个雨季
    2021-01-24 06:52

    If you want the code to work with PersonOptionalAddress you should add one more instance of the type class so that it will work also with Map( "name" -> "Tom", "address" -> Some(Map ...) )

    implicit def hconsFromMap0opt[K <: Symbol, V, R <: HList, T <: HList](implicit
                                                                              witness: Witness.Aux[K],
                                                                              gen: LabelledGeneric.Aux[V, R],
                                                                              fromMapH: FromMap[R],
                                                                              fromMapT: FromMap[T]
                                                                             ): FromMap[FieldType[K, Option[V]] :: T] =
          new FromMap[FieldType[K, Option[V]] :: T] {
            def apply(m: Map[String, Any]): Option[FieldType[K, Option[V]] :: T] = (for {
              v <- m.get(witness.value.name)
              r <- Typeable[Map[String, Any]].cast(v)
              h <- fromMapH(r)
              t <- fromMapT(m)
            } yield field[K](Some(gen.from(h))) :: t).orElse(for {
              v <- m.get(witness.value.name)
              r1 <- Typeable[Option[Map[String, Any]]].cast(v)
              opt = for {
                r <- r1
                h <- fromMapH(r)
              } yield gen.from(h)
              t <- fromMapT(m)
            } yield field[K](opt) :: t)
          }
    

    The whole code

    import shapeless._
    import labelled.{FieldType, field}
    
    object App {
    
      trait FromMap[L <: HList] {
        def apply(m: Map[String, Any]): Option[L]
      }
    
      trait LowPriorityFromMap {
        implicit def hconsFromMap1[K <: Symbol, V, T <: HList](implicit
                                                               witness: Witness.Aux[K],
                                                               typeable: Typeable[V],
                                                               fromMapT: Lazy[FromMap[T]]
                                                              ): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {
          def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
            v <- m.get(witness.value.name)
            h <- typeable.cast(v)
            t <- fromMapT.value(m)
          } yield field[K](h) :: t
        }
      }
    
      object FromMap extends LowPriorityFromMap {
        implicit val hnilFromMap: FromMap[HNil] = new FromMap[HNil] {
          def apply(m: Map[String, Any]): Option[HNil] = Some(HNil)
        }
    
        implicit def hconsFromMap0[K <: Symbol, V, R <: HList, T <: HList](implicit
                                                                           witness: Witness.Aux[K],
                                                                           gen: LabelledGeneric.Aux[V, R],
                                                                           fromMapH: FromMap[R],
                                                                           fromMapT: FromMap[T]
                                                                          ): FromMap[FieldType[K, V] :: T] =
          new FromMap[FieldType[K, V] :: T] {
            def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
              v <- m.get(witness.value.name)
              r <- Typeable[Map[String, Any]].cast(v)
              h <- fromMapH(r)
              t <- fromMapT(m)
            } yield field[K](gen.from(h)) :: t
          }
    
        implicit def hconsFromMap0opt[K <: Symbol, V, R <: HList, T <: HList](implicit
                                                                              witness: Witness.Aux[K],
                                                                              gen: LabelledGeneric.Aux[V, R],
                                                                              fromMapH: FromMap[R],
                                                                              fromMapT: FromMap[T]
                                                                             ): FromMap[FieldType[K, Option[V]] :: T] =
          new FromMap[FieldType[K, Option[V]] :: T] {
            def apply(m: Map[String, Any]): Option[FieldType[K, Option[V]] :: T] = (for {
              v <- m.get(witness.value.name)
              r <- Typeable[Map[String, Any]].cast(v)
              h <- fromMapH(r)
              t <- fromMapT(m)
            } yield field[K](Some(gen.from(h))) :: t).orElse(for {
              v <- m.get(witness.value.name)
              r1 <- Typeable[Option[Map[String, Any]]].cast(v)
              opt = for {
                r <- r1
                h <- fromMapH(r)
              } yield gen.from(h)
              t <- fromMapT(m)
            } yield field[K](opt) :: t)
          }
      }
    
      trait CaseClassFromMap[P <: Product] {
        def apply(m: Map[String, Any]): Option[P]
      }
    
      object CaseClassFromMap {
        implicit def mk[P <: Product, R <: HList](implicit gen: LabelledGeneric.Aux[P, R],
                                                  fromMap: FromMap[R]): CaseClassFromMap[P] = new CaseClassFromMap[P] {
          def apply(m: Map[String, Any]): Option[P] = fromMap(m).map(gen.from)
        }
    
        def apply[P <: Product](map: Map[String, Any])(implicit fromMap: CaseClassFromMap[P]): P = fromMap(map).get
      }
    
      case class Address(street: String, zip: Int)
      case class PersonOptionalAddress(name: String, address: Option[Address])
      case class PersonAddress(name: String, address: Address)
    
      def main(args: Array[String]): Unit = {
        println(
          CaseClassFromMap[PersonAddress](Map(
            "name" -> "Tom",
            "address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
          ))
        )//PersonAddress(Tom,Address(Jefferson st,10000))
    
        println(
          CaseClassFromMap[PersonOptionalAddress](Map(
            "name" -> "Tom",
            "address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
          ))
        )//PersonOptionalAddress(Tom,Some(Address(Jefferson st,10000)))
    
        println(
          CaseClassFromMap[PersonOptionalAddress](Map(
            "name" -> "Tom",
            "address" -> Some(Map("street" -> "Jefferson st", "zip" -> 10000))
          ))
        )//PersonOptionalAddress(Tom,Some(Address(Jefferson st,10000)))
    
        println(
          CaseClassFromMap[PersonOptionalAddress](Map(
            "name" -> "Tom",
            "address" -> None
          ))
        )//PersonOptionalAddress(Tom,None)
    
      }
    }
    

    0 讨论(0)
提交回复
热议问题