0

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?

Christian Bongiorno
  • 5,150
  • 3
  • 38
  • 76
  • Does this answer your question? [Right way to write JSON deserializer in Spring or extend it](https://stackoverflow.com/questions/11376304/right-way-to-write-json-deserializer-in-spring-or-extend-it) – aSemy Dec 27 '22 at 11:44
  • You could deserialize it as an List containing just one element. If you want I can write an answer involving java code (I don't know kotlin). – dariosicily Dec 27 '22 at 18:47
  • @dariosicily - that could prove instructional. Thanks – Christian Bongiorno Dec 28 '22 at 18:00

2 Answers2

0

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 = """
       {"items": [
        {
           "foo" : "baz",
           "whatever" : "thing"
        }
        ]}
    """.trimIndent()
    val items: JsonNode = mapper.readTree(json).get("items")
    val bugs = mapper.readValue<Array<Bug>>(items.toPrettyString())  
}
dariosicily
  • 4,239
  • 2
  • 11
  • 17
0

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:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146