Jackson Deserializer 在失败时默认为 null
Jackson Deserializer default to null on failure
我遇到一个问题,上游服务正在发出请求,发出的请求中约有 5% 部分格式不正确。例如,我得到的不是可空的 属性 作为我期望的对象类型,而是一个随机字符串。
data class FooDTO(
val bar: BarDTO?,
val name: String
)
data class BarDTO(
val size: Int
)
但我得到的 payload 看起来像
{
"name": "The Name",
"bar": "uh oh random string instead of object"
}
我不想在发生这种情况时使请求失败,因为正确的数据部分对我的目的仍然有用,所以我想做的只是将反序列化失败默认为 null
.我的 FooDTO
中也有一些不同的子对象可以执行此操作,因此我想要一种通用方法来解决这些特定领域的问题。我知道我可以像下面这样编写一个自定义解串器来一次性解决它。
class BarDtoDeserializer @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer<BarDTO?>(vc) {
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): AnalysisDTO? {
return try {
val node = jp.codec.readTree<JsonNode>(jp)
BarDTO(size = node.get("size").numberValue().toInt())
} catch (ex: Throwable) {
null
}
}
}
然后我可以用 @JsonDeserialize(using=BarDtoDeserializer::class)
装饰我的 BarDTO
对象以强制它使用该反序列化器。我希望做的是有一些通用的方法来做到这一点。
感谢这个答案 I've found that a DeserializationProblemHandler
可用于 return 'null' 无效数据。
在您的实例中,覆盖 handleMissingInstantiator()
函数。如果其他负载有其他类型的错误数据,则可能需要其他覆盖。
此外,我认为 CoercionConfig
可能是答案,但我无法让它工作或找到合适的文档。那可能是另一条路。
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
import com.fasterxml.jackson.databind.deser.ValueInstantiator
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
data class FooDTO(
val name: String,
val bar: BarDTO?,
)
data class BarDTO(
val size: Int
)
fun main() {
val mapper = jacksonObjectMapper()
// mapper
// .coercionConfigFor(LogicalType.Textual)
// .setCoercion(CoercionInputShape.String, CoercionAction.AsNull)
mapper.addHandler(object : DeserializationProblemHandler() {
override fun handleMissingInstantiator(
ctxt: DeserializationContext?,
instClass: Class<*>?,
valueInsta: ValueInstantiator?,
p: JsonParser?,
msg: String?
): Any? {
println("returning null for value, $msg")
return null
}
})
val a: FooDTO = mapper.readValue(
"""
{
"name": "I'm variant A",
"bar": "uh oh random string instead of object"
}
"""
)
val b: FooDTO = mapper.readValue(
"""
{
"name": "I'm variant B",
"bar": {
"size": 2
}
}
"""
)
println(a)
println(b)
assert(a.bar == null)
assert(a.bar?.size == 2)
}
输出:
returning null for value, no String-argument constructor/factory method to deserialize from String value ('uh oh random string instead of object')
FooDTO(name=I'm variant A, bar=null)
FooDTO(name=I'm variant B, bar=BarDTO(size=2))
Process finished with exit code 0
我遇到一个问题,上游服务正在发出请求,发出的请求中约有 5% 部分格式不正确。例如,我得到的不是可空的 属性 作为我期望的对象类型,而是一个随机字符串。
data class FooDTO(
val bar: BarDTO?,
val name: String
)
data class BarDTO(
val size: Int
)
但我得到的 payload 看起来像
{
"name": "The Name",
"bar": "uh oh random string instead of object"
}
我不想在发生这种情况时使请求失败,因为正确的数据部分对我的目的仍然有用,所以我想做的只是将反序列化失败默认为 null
.我的 FooDTO
中也有一些不同的子对象可以执行此操作,因此我想要一种通用方法来解决这些特定领域的问题。我知道我可以像下面这样编写一个自定义解串器来一次性解决它。
class BarDtoDeserializer @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer<BarDTO?>(vc) {
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): AnalysisDTO? {
return try {
val node = jp.codec.readTree<JsonNode>(jp)
BarDTO(size = node.get("size").numberValue().toInt())
} catch (ex: Throwable) {
null
}
}
}
然后我可以用 @JsonDeserialize(using=BarDtoDeserializer::class)
装饰我的 BarDTO
对象以强制它使用该反序列化器。我希望做的是有一些通用的方法来做到这一点。
感谢这个答案 I've found that a DeserializationProblemHandler
可用于 return 'null' 无效数据。
在您的实例中,覆盖 handleMissingInstantiator()
函数。如果其他负载有其他类型的错误数据,则可能需要其他覆盖。
此外,我认为 CoercionConfig
可能是答案,但我无法让它工作或找到合适的文档。那可能是另一条路。
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
import com.fasterxml.jackson.databind.deser.ValueInstantiator
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
data class FooDTO(
val name: String,
val bar: BarDTO?,
)
data class BarDTO(
val size: Int
)
fun main() {
val mapper = jacksonObjectMapper()
// mapper
// .coercionConfigFor(LogicalType.Textual)
// .setCoercion(CoercionInputShape.String, CoercionAction.AsNull)
mapper.addHandler(object : DeserializationProblemHandler() {
override fun handleMissingInstantiator(
ctxt: DeserializationContext?,
instClass: Class<*>?,
valueInsta: ValueInstantiator?,
p: JsonParser?,
msg: String?
): Any? {
println("returning null for value, $msg")
return null
}
})
val a: FooDTO = mapper.readValue(
"""
{
"name": "I'm variant A",
"bar": "uh oh random string instead of object"
}
"""
)
val b: FooDTO = mapper.readValue(
"""
{
"name": "I'm variant B",
"bar": {
"size": 2
}
}
"""
)
println(a)
println(b)
assert(a.bar == null)
assert(a.bar?.size == 2)
}
输出:
returning null for value, no String-argument constructor/factory method to deserialize from String value ('uh oh random string instead of object')
FooDTO(name=I'm variant A, bar=null)
FooDTO(name=I'm variant B, bar=BarDTO(size=2))
Process finished with exit code 0