问题描述
我正在尝试创建一个动态解析器,它允许我根据类名将 json 内容解析为不同的类.
I am trying to create a dynamic parser which allows me to parse json content into different classes depending on a class name.
我会得到 json 和类名(作为字符串),我想做这样的事情:
I will get the json and the class name (as String) and I would like to do something like this:
val theCaseClassName = "com.ardlema.JDBCDataProviderProperties"
val myCaseClass = Class.forName(theCaseClassName)
val jsonJdbcProperties = """{"url":"myUrl","userName":"theUser","password":"thePassword"}"""
val json = Json.parse(jsonJdbcProperties)
val value = Try(json.as[myClass])
上面的代码显然无法编译,因为 json.as[] 方法试图将节点转换为T"(我为我的案例类定义了一个隐式的 Reads[T])
The above code obviously does not compile because the json.as[] method tries to convert the node into a "T" (I have an implicit Reads[T] defined for my case class)
从原始字符串中获取正确的T"以传递给 json.as[] 方法的最佳方法是什么?
What would be the best way to get a proper "T" to pass in to the json.as[] method from the original String?
推荐答案
一个可能有效的很好的解决方案是进行多态反序列化.这允许您向 json 添加一个字段(如类型")并允许 Jackson(假设您重新使用一个很棒的 json 解析器(如 Jackson)来代表您找出正确的类型.看起来您可能没有使用 Jackson;我保证它值得使用.
A great solution that might work would be to do polymorphic deserialization. This allows you to add a field (like "type") to your json and allow Jackson (assuming you're using an awesome json parser like Jackson) to figure out the proper type on your behalf. It looks like you might not be using Jackson; I promise it's worth using.
这篇帖子提供了很好的介绍到多态类型.它涵盖了许多有用的情况,包括您无法修改第 3 方代码的情况(这里您添加了一个 Mixin 来注释类型层次结构).
This post gives a great introduction to polymorphic types. It covers many useful cases including the case where you can't modify 3rd party code (here you add a Mixin to annotate the type hierarchy).
最简单的情况最终看起来像这样(所有这些也适用于 Scala 对象——jackson 甚至有一个很棒的 Scala 模块):
The simplest case ends up looking like this (and all of this works great with Scala objects too -- jackson even has a great scala module):
object Test {
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(Array(
new Type(value = classOf[Cat], name = "cat"),
new Type(value = classOf[Dog], name = "dog")
))
trait Animal
case class Dog(name: String, breed: String, leash_color: String) extends Animal
case class Cat(name: String, favorite_toy: String) extends Animal
def main(args: Array[String]): Unit = {
val objectMapper = new ObjectMapper with ScalaObjectMapper
objectMapper.registerModule(DefaultScalaModule)
val dogStr = """{"type": "dog", "name": "Spike", "breed": "mutt", "leash_color": "red"}"""
val catStr = """{"type": "cat", "name": "Fluffy", "favorite_toy": "spider ring"}"""
val animal1 = objectMapper.readValue[Animal](dogStr)
val animal2 = objectMapper.readValue[Animal](catStr)
println(animal1)
println(animal2)
}
}
这会生成以下输出:
// Dog(Spike,mutt,red)
// Cat(Fluffy,spider ring)
您也可以避免列出子类型映射,但它要求json类型"字段稍微复杂一些.试验一下;你可能会喜欢.像这样定义动物:
You can also avoid listing the subtype mapping, but it requires that the json "type" field is a bit more complex. Experiment with it; you might like it. Define Animal like this:
@JsonTypeInfo(
use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
trait Animal
它会像这样产生(并消耗)json:
And it produces (and consumes) json like this:
/*
{
"breed": "mutt",
"leash_color": "red",
"name": "Spike",
"type": "classpath.to.Test$Dog"
}
{
"favorite_toy": "spider ring",
"name": "Fluffy",
"type": "classpath.to.Test$Cat"
}
*/
这篇关于Scala - 从字符串动态实例化类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!