I have a payload like this:
items: [
{
"foo" : "baz",
"whatever" : "thing"
}
]
Literally, all I have to do is just navigate to /items/0
and then continue the normal deserialization process. But I don't see how I can do that with the current JsonDeserializer.
class BugDeserializer : JsonDeserializer<Bug>() {
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Bug {
val node: TreeNode = p!!.readValueAsTree()
val correct = node.at("/items/0")
// Now what? 'correct' has no `readValueAs` method
return p.readValueAs(Bug::class.java)
}
}
Once I navigate properly, I don't see anyway to thus "continue". I have gone so far as to instantiate another ObjectMapper
to do the job, but this doesn't work either because I have the deserializer directly on my Bug
class, so it gets invoked twice.
How do I simply deserialize normally, once I've navigated to the correct json path?
CodePudding user response:
To override custom deserialiser and provide your own you need to use BeanDeserializerModifier
. You can find example here:
Also you can implement a new custom deserialiser, skip the beginning of JSON
payload, deserialise to an inner class and return what you want. It could look like below:
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.type.SimpleType
class BugJsonDeserializer : JsonDeserializer<Bug>() {
private val innerBugType: JavaType = SimpleType.constructUnsafe(InnerBug::class.java)
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Bug {
p.nextToken() // start object
p.nextToken() // field name
p.nextToken() // start array
val innerDeser: JsonDeserializer<*> = ctxt.findNonContextualValueDeserializer(innerBugType)
val (foo, whatever) = innerDeser.deserialize(p, ctxt) as InnerBug
return Bug(foo, whatever)
}
private data class InnerBug(
val foo: String? = null,
val whatever: String? = null
)
}
You need to register above deserializer:
@JsonDeserialize(using = BugJsonDeserializer::class)
data class Bug(
...
See also:
CodePudding user response:
If you have an input {"items": [{"foo" : "baz","whatever" : "thing"}]}
json file and you want to deserialize it to a Bug
list where Bug
class is like data class Bug(var foo: String, var whatever: String)
you can use TypeReference
to instantiate reference to the generic type List<Bug>
like below:
data class Bug(var foo: String, var whatever: String)
fun main() {
val mapper = ObjectMapper().registerKotlinModule()
val json = """
{"items": [
{
"foo" : "baz",
"whatever" : "thing"
}
]}
""".trimIndent()
val items: JsonNode = mapper.readTree(json).get("items")
val bugs: List<Bug> = mapper.convertValue(items, object: TypeReference<List<Bug>>(){})
}
Edit:
A simpler solution converting a json array into a Array<Bug>
object can be obtained using the com.fasterxml.jackson.module.kotlin.readValue
with one line:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
fun main() {
val mapper = jacksonObjectMapper()
val json = """
[
{
"foo" : "baz",
"whatever" : "thing"
}
]
""".trimIndent()
//automatically infer the kotlin Array<Bug> type
val bugs = mapper.readValue<Array<Bug>>(json)
}