Home > Blockchain >  Jackson Custom Serializer simplification
Jackson Custom Serializer simplification

Time:12-29

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)    
}
  • Related