How to convert generic potentially nested map Map[String, Any] to case class using any library in Scala?

后端 未结 3 2142
挽巷
挽巷 2021-01-17 09:27

I\'ve not had much joy with reflection, this answer using shapeless works for some cases (but seems to have many edge cases) Shapeless code to convert Map[String, Any] to ca

3条回答
  •  余生分开走
    2021-01-17 09:40

    We can use circe

    import io.circe._
    import io.circe.generic.auto._
    import io.circe.parser._
    import io.circe.syntax._
    
    
    def mapToJson(map: Map[String, Any]): Json =
        map.mapValues(anyToJson).asJson
    
      def anyToJson(any: Any): Json = any match {
        case n: Int => n.asJson
        case n: Long => n.asJson
        case n: Double => n.asJson
        case s: String => s.asJson
        case true => true.asJson
        case false => false.asJson
        case null | None => None.asJson
        case list: List[_] => list.map(anyToJson).asJson
        case list: Vector[_] => list.map(anyToJson).asJson
        case Some(any) => anyToJson(any)
        case map: Map[String, Any] => mapToJson(map)
      }
    
    def mapToCaseClass[T : Decoder](map: Map[String, Any]): T = mapToJson(map).as[T].right.get
    

    Then, if we have any types that are not primitive, we just need to add these to our anyToJson along with an encoder/decoder pair that can encode/decode this type as something primitive.

    E.g. we can represent java.sql.Timestamp with Long, then

    import cats.syntax.either._
    
      import io.circe.Decoder
      import io.circe.Encoder
    
      implicit val decodeTimestamp: Decoder[Timestamp] = Decoder.decodeLong.emap(long =>
        Either.catchNonFatal(new Timestamp(long)).leftMap(_ => "Timestamp")
      )
    
    implicit val encodeTimestamp: Encoder[Timestamp] = Encoder.encodeLong.contramap[Timestamp](_.getTime)
    

    and we need to add the line to anyToJson

    case n: Timestamp => n.asJson
    

提交回复
热议问题