我有一个字符串形式的JSON内容,我首先要通过Jackson进行编程遍历。然后,当我有感兴趣的节点时,我想对其进行反序列化。
我已经使用mapper.readValue成功反序列化了字符串,但是现在我想在jsonNode而不是字符串上执行这样的操作。
package somepackage
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.treeToValue
fun main() {
val mapper = ObjectMapper().registerModule(KotlinModule())
readValueWorksFine(mapper)
treeToValueFails(mapper)
}
fun treeToValueFails(mapper: ObjectMapper) {
val fullJsonContent = """
[{
"product_id":123,
"Comments":
[{
"comment_id": 23,
"message": "Hello World!"
}]
}]
""".trimIndent()
// Traverse to get the node of interest
val commentsNode: JsonNode = mapper.readTree(fullJsonContent).get(0).get("Comments")
// Deserialize
val comments: List<Comment> = mapper.treeToValue<List<Comment>>(commentsNode)
// The line below fails. (I would have expected the exception to be thrown in the line above instead.
// Exception:
// Exception in thread "main" java.lang.ClassCastException: class
// java.util.LinkedHashMap cannot be cast to class somepackage.Comment (java.util.LinkedHashMap is in module
// java.base of loader 'bootstrap'; somepackage.Comment is in unnamed module of loader 'app')
for (comment: Comment in comments) { // This line fails
println(comment.comment_id)
println(comment.message)
}
}
fun readValueWorksFine(mapper: ObjectMapper) {
val commentsJsonContent = """
[{
"comment_id": 23,
"message": "Hello World!"
}]
""".trimIndent()
val comments1: List<Comment> = mapper.readValue<List<Comment>>(commentsJsonContent)
for (comment in comments1) {
println(comment)
}
}
data class Comment(val comment_id: Long, val message: String)
上面的代码导致以下异常/输出:
Comment(comment_id=23, message=Hello World!)
Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class somepackage.Comment (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; somepackage.Comment is in unnamed module of loader 'app')
at somepackage.TKt.treeToValueFails(T.kt:39)
at somepackage.TKt.main(T.kt:13)
at somepackage.TKt.main(T.kt)
答案 0 :(得分:0)
即使ObjectMapper.treeToValue
是具有经过修改的泛型参数(这意味着泛型在运行时保留)的Kotlin内联扩展函数,它仍会调用Java ObjectMapper.treeToValue(TreeNode, Class<T>)
方法。由于类型擦除,传递为Class<T>
的值将丢失诸如List<Comment>
之类的通用类型的通用类型信息。
因此treeToValue
可用于:
mapper.treeToValue<Comment>(commentNode)
但不是:
mapper.treeToValue<List<Comment>>(commentsNode)
还要注意,ObjectMapper
包含多个带有@SuppressWarnings
批注的方法,这会导致一些问题不在编译时出现,而是在运行时出现。
这是最好的解决方案。它使用Kotlin扩展功能ObjectMapper.convertValue
。
val commentsNode = mapper.readTree(fullJsonContent).get(0).get("Comments")
val comments = mapper.convertValue<List<Comment>>(commentsNode)
此解决方案不使用jackson-module-kotlin
扩展功能。
val reader = mapper.readerFor(object : TypeReference<List<Comment>>() {})
val comments: List<Comment> = reader.readValue(commentsNode)
由于treeToValue
(Kotlin扩展函数)确实适用于非泛型类型,因此您可以首先将节点作为JsonNode的列表获取,然后将每个JsonNode映射到Comment。
但是您不能简单地返回mapper.treeToValue(it)
是很麻烦的,因为这会导致类型推断编译错误。
val commentsNode = mapper.readTree(fullJsonContent).get(0).get("Comments")
val comments = commentsNode.elements().asSequence().toList().map {
val comment: Comment = mapper.treeToValue(it)
comment
}