[英]How to convert generic potentially nested map Map[String, Any] to case class using any library in Scala?
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 case class cannot handle optional substructures我对反射并没有很满意,这个答案使用 shapeless 对某些情况有效(但似乎有很多边缘情况) Shapeless code to convert Map[String, Any] to case class cannot handle optional substructures
Does anyone know of a nice library that does this in just a few LOCs?有谁知道一个不错的库可以在几个 LOC 中做到这一点?
I've found a reasonably neat way to do it using Spray Json我找到了一种使用 Spray Json 的相当简洁的方法
First we define a way to get to a JsObject
from a Map[String, Any]
首先,我们定义了一种从Map[String, Any]
获取JsObject
的方法
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)
}
Then we can just use convertTo
provided we have the implicit JsonFormat
in scope然后我们可以只使用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))
}
Spray json only has out of box jsonFormat up to 22 fields! Spray json 只有开箱即用的 jsonFormat 最多 22 个字段!
Can not handle any custom types, eg java.sql.Timestamp
, since this isn't a JSON type.无法处理任何自定义类型,例如java.sql.Timestamp
,因为这不是 JSON 类型。
Using jackson:使用杰克逊:
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]))
Output: Bar(3,foo,Foo(List(1, 2),None))
输出: Bar(3,foo,Foo(List(1, 2),None))
We can use circe我们可以使用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.然后,如果我们有任何不是原始类型的类型,我们只需要将它们添加到我们的anyToJson
以及可以将这种类型编码/解码为原始类型的编码器/解码器对。
Eg we can represent java.sql.Timestamp
with Long
, then例如,我们可以用Long
表示java.sql.Timestamp
,然后
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
我们需要anyToJson
一行添加到anyToJson
case n: Timestamp => n.asJson
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.