Как преобразовать общий потенциально вложенный Map [String, Any] в case-класс, используя любую библиотеку в Scala? - PullRequest
0 голосов
/ 07 марта 2019

У меня не было большой радости с отражением, этот ответ использует бесформенные работы для некоторых случаев (но, похоже, имеет много краевых случаев) Бесформенный код для преобразования Map [String, Any] в класс case не может обрабатывать необязательные подструктуры

Кто-нибудь знает хорошую библиотеку, которая делает это всего за несколько LOC?

Ответы [ 3 ]

1 голос
/ 07 марта 2019

Использование Джексона:

libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.8"
libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.8"
case class Foo(a: List[Int], b: Option[Double])
case class Bar(c: Int, d: String, e: Foo)

val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
println(mapper.convertValue(Map(
  "c" -> 3, 
  "d" -> "foo", 
  "e" -> Map("a" -> List(1, 2))), classOf[Bar]))

Выход: Bar(3,foo,Foo(List(1, 2),None))

1 голос
/ 07 марта 2019

Я нашел достаточно удобный способ сделать это с помощью Spray Json

Сначала мы определим способ получить JsObject из Map[String, Any]

def mapToJsObject(map: Map[String, Any]): JsObject =
  JsObject(fields = map.mapValues(anyToJsValue))

def anyToJsValue(any: Any): JsValue = any match {
  case n: Int => JsNumber(n)
  case n: Long => JsNumber(n)
  case n: Double => JsNumber(n)
  case s: String => JsString(s)
  case true => JsTrue
  case false => JsFalse
  case null | None => JsNull
  case list: List[_] => JsArray(list.map(anyToJsValue).toVector)
  case Some(any) => anyToJsValue(any)
  case map: Map[String, Any] => mapToJsObject(map)
}

Затеммы можем просто использовать convertTo при условии, что у нас есть неявное JsonFormat в области действия

case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)

implicit val addressFormat = jsonFormat2(Address.apply)
implicit val personFormat = jsonFormat2(Person.apply)

"Convert Person example map to Person JsObject" in {
  JsonUtils.mapToJsObject(
    Map(
      "name" -> "Tom",
      "address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
    )
  ).convertTo[Person] must_=== Person("Tom", Address("Jefferson st", 10000))
}

CAVEATs

Спрей json имеет из коробки только jsonFormat до 22 полей!

Не может обрабатывать любые пользовательские типы, например java.sql.Timestamp, так как это не тип JSON.

0 голосов
/ 26 марта 2019

Мы можем использовать 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

Тогда, если у нас есть какие-либо типы, которые не являются примитивными, нам просто нужно добавить их в нашу anyToJson вместе с парой кодер / декодер, которая может кодировать / декодировать этот тип как нечто примитивное.

например. мы можем представить java.sql.Timestamp с Long, затем

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)

и нам нужно добавить строку к anyToJson

case n: Timestamp => n.asJson
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...