我有很多不同的外部JSON实体,我想通过json4s(scala)解析到不同的内部案例类。通过json4s的提取功能,一切正常。我已经实现了一个parse函数,它接受一个类型和一个json字符串,并将字符串解析为类型/ case类。为了将正确的json字符串映射到正确的case类,我实现了一个模式匹配函数,它看起来像这个
entityName match {
case "entity1" => JsonParser.parse[Entity1](jsonString)
case "entity2" => JsonParser.parse[Entity2](jsonString)
....
我不喜欢这里的重复,并希望通过这样的地图进行映射:
val mapping = Map(
"entity1" -> Entity1,
"entity2" -> Entity2
....
有了这个地图,我可以像这样只执行一次JsonParser.parse函数
JsonParser.parse[mapping(entityName)](jsonString)
这不起作用,因为地图引用了对象而不是类类型。我也尝试过classOf [Entity1],但这也行不通。有没有办法做到这一点?
谢谢!
答案 0 :(得分:1)
Scala无法实现JsonParser.parse
工作的方式。 Scala是一种强大且静态类型的语言。这意味着编译器应该在编译时知道值的类型,以便能够验证您是否只访问它们上的有效字段和方法和/或将它们作为有效参数传递给方法。假设你的课程是
case class Entity1(value:Int, unique1:Int)
case class Entity2(value:String, unique2:String)
你写了
val parsed = JsonParser.parse[mapping("entity1")](jsonString)
编译器如何知道parsed
的类型以了解parsed.value
的类型或知道parsed.unique1
是有效字段而parsed.unique2
不是?最好的类型编译器可以分配给这样的parsed
是非常通用的,如Any
。当然,您可以稍后将Any
转发给特定类型,但这意味着您仍然必须在asInstanceOf
中明确指定该类型,这种类型会破坏整个目的。不过,如果以某种方式返回Any
对你来说没问题,你可以尝试这样做:
import org.json4s.jackson.JsonMethods
implicit val formats = org.json4s.DefaultFormats // or whatever Formats you actually need
val typeMap: Map[String, scala.reflect.Manifest[_]] = Map(
"String" -> implicitly[scala.reflect.Manifest[String]],
"Int" -> implicitly[scala.reflect.Manifest[Int]]
)
def parseAny(typeName: String, jsonString: String): Any = {
val jValue = JsonMethods.parse(jsonString)
jValue.extract(formats, typeMap(typeName))
}
然后做这样的事情:
def testParseByTypeName(typeName: String, jsonString: String): Unit = {
try {
val parsed = parseAny(typeName, jsonString)
println(s"parsed by name $typeName => ${parsed.getClass} - '$parsed'")
} catch {
case e => println(e)
}
}
def test() = {
testParseByTypeName("String", "\"abc\"")
testParseByTypeName("Int", "123")
}
P.S。如果您的entityName
并非来自外部(即您不会分析数据以找出实际类型),那么您根本不需要它。使用类型(不需要match
/ case
)就足够了,例如:
def parse[T](jsonString: String)(implicit mf: scala.reflect.Manifest[T]): T = {
val jValue = JsonMethods.parse(jsonString)
jValue.extract[T]
}
def testParse[T](prefix: String, jsonString: String)(implicit mf: scala.reflect.Manifest[T]): Unit = {
try {
val parsed = parse[T](jsonString)
println(s"$prefix => ${parsed.getClass} - '$parsed'")
} catch {
case e => println(e)
}
}
def test() = {
testParse[String]("parse String", "\"abc\"")
testParse[Int]("parse Int", "123")
}
答案 1 :(得分:0)
从@SergGr获取想法,作为粘贴在Ammonite REPL上的代码片段:
{
import $ivy.`org.json4s::json4s-native:3.6.0-M2`
import org.json4s.native.JsonMethods.parse
import org.json4s.DefaultFormats
import org.json4s.JValue
case class Entity1(name : String, value : Int)
case class Entity2(name : String, value : Long)
implicit val formats = DefaultFormats
def extract[T](input : JValue)(implicit m : Manifest[T]) = input.extract[T]
val mapping: Map[String, Manifest[_]] = Map(
"entity1" -> implicitly[Manifest[Entity1]],
"entity2" -> implicitly[Manifest[Entity2]]
)
val input = parse(""" { "name" : "abu", "value" : 1 } """)
extract(input)(mapping("entity1")) //Entity1("abu", 1)
extract(input)(mapping("entity2")) //Entity2("abu", 1L)
}