【问题标题】:How to convert JSON to scala shapeless.hlist?如何将 JSON 转换为 scala shapeless.hlist?
【发布时间】:2020-09-28 13:05:27
【问题描述】:

我得到了像 {"name":"susan","age":25} 这样的 json,以及像 "name:String,age:Int" 这样的 json 键集提示,如何从该 json 创建 HList?

【问题讨论】:

  • Shapeless 是编译安全的,JSON 不是。为了清楚起见,你不能做任何“自动”的事情(即你不能做一些会神奇地猜测 JSON 字符串类型的东西)
  • 这里有 Dave Gurnell 的无形之书(第 5 章)的简介:stackoverflow.com/a/62177781/6802156
  • 我建议你看看像 circe 这样的任何 json 库。
  • 这听起来像是另一个问题。最好用所有细节来扩展这个问题,或者关闭这个问题并打开一个新问题以引起更多关注。无论如何,我不知道 Flink,所以我不能再帮忙了,对不起。
  • @LuisMiguelMejíaSuárez "像 circe 这样的库读取一个 json 并将其转换为 HList,然后它可以将其转换为案例类" 一种 hlist (String :: Int :: HNil) 或案例类(A for case class A(s: String, i: Int))必须在编译时知道。但是提示"name:String,age:Int" 似乎是一个运行时字符串。

标签: scala reflection apache-flink shapeless hlist


【解决方案1】:

根据您添加然后删除的code,似乎具有运行时字符串提示"name:String,age:Int" 和运行时字符串json {"name":"susan","age":25},您希望获得HListruntime reflection。你可以这样做

import shapeless.HList
import scala.reflect.runtime
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val tb = runtime.currentMirror.mkToolBox()

val jsonStr = """{"name":"susan","age":25}"""
val hint = "name:String,age:Int"
val classType = tb.define(tb.parse(s"case class Test($hint)").asInstanceOf[ImplDef]).asClass.toType
val hlist = tb.eval(q"""
  import io.circe.generic.auto._
  import io.circe.parser.decode
  val classInstance = decode[$classType]($jsonStr)

  import shapeless.Generic
  Generic[$classType].to(classInstance.toOption.get)
""").asInstanceOf[HList]
println(hlist) // susan :: 25 :: HNil

请注意,您在运行时执行所有操作,因此您将无法在编译时访问类型 String :: Int :: HNil 并且 hlist 具有静态类型只是 HList(不是 String :: Int :: HNil)而 HList 实际上不是比 List[Any] 更好。

build.sbt

libraryDependencies ++= Seq(
  scalaOrganization.value % "scala-reflect"  % scalaVersion.value,
  scalaOrganization.value % "scala-compiler" % scalaVersion.value,
  "com.chuusai" %% "shapeless" % "2.4.0-M1",
  "io.circe" %% "circe-core"    % "0.13.0",
  "io.circe" %% "circe-parser"  % "0.13.0",
  "io.circe" %% "circe-generic" % "0.13.0"
)

实际上,我猜我们做了一些奇怪的事情。我们使用高度类型级别的库 (shapeless, circe) 旨在实现静态类型安全,然后在运行时运行它们,忽略类型安全并通过反射运行它们并实际获取 List[Any] (HList)。

我猜如果List[Any](字段值列表)对您来说足够了,那么您只需要使用更多的运行时库。例如,json4s

import org.json4s.{JInt, JObject, JString, JValue}
import org.json4s.jackson.JsonMethods._

val jsonStr: String = """{"name":"susan","age":25}"""
val json: JValue = parse(jsonStr) //JObject(List((name,JString(susan)), (age,JInt(25))))
val l: List[JValue] = json.asInstanceOf[JObject].obj.map(_._2) //List(JString(susan), JInt(25))
val res: List[Any] = l.map {
  case JString(s) => s
  case JInt(n)    => n
} //List(susan, 25)

build.sbt

libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.6.9"

实际上,Circe 也可以做到这一点,只是使用 parse 而不是 decode[A]

import io.circe.{Json, JsonNumber}
import io.circe.parser.parse

val jsonStr: String = """{"name":"susan","age":25}"""
val json: Json = parse(jsonStr).toOption.get //{"name":"susan","age":25}
val l: List[Json] = json.asObject.get.values.toList //List("susan", 25)
val res: List[Any] = l.map(_.fold[Any](null, null, (_: JsonNumber).toInt.get, identity[String], null, null)) //List(susan, 25)

如果您需要案例类或元组的实例而不是 HList 替换

tb.eval(q"""
  import io.circe.generic.auto._
  import io.circe.parser.decode
  val classInstance = decode[$classType]($jsonStr)

  import shapeless.Generic
  Generic[$classType].to(classInstance.toOption.get)
""").asInstanceOf[HList] // susan :: 25 :: HNil

tb.eval(q"""
  import io.circe.generic.auto._
  import io.circe.parser.decode
  decode[$classType]($json).toOption.get
""").asInstanceOf[Product] //Test(susan,25)

tb.eval(q"""
  import io.circe.generic.auto._
  import io.circe.parser.decode
  val classInstance = decode[$classType]($json)

  import shapeless.Generic
  Generic[$classType].to(classInstance.toOption.get).tupled
""").asInstanceOf[Product] //(susan,25)

相应地。

【讨论】:

    猜你喜欢
    • 2021-04-19
    • 2021-03-20
    • 1970-01-01
    • 2014-05-23
    • 2020-09-23
    • 2014-04-04
    • 2015-03-12
    • 2020-07-11
    • 2012-08-07
    相关资源
    最近更新 更多