繁体   English   中英

使用jacksonObjectMapper将JSON反序列化为不同类型

[英]Deserialize JSON to Different types with jacksonObjectMapper

假设我从服务器收到JSON错误,该错误可以采用两种不同的结构:

{"errorMessage": "This action is unauthorized."}

要么

{"errorMessage":{"changePassword":"Old password is incorrect."}}

我如何反序列化这种json?

我尝试了什么

我试图让抽象类“错误”和两个孩子:

abstract class Error() {}

data class SingleError(val errorMessage: String) : Error() 

data class MultiError(val errorMessage: Map<String, String>) : Error() 

然后我尝试:

jacksonObjectMapper().readValue<Error>(response.body)

反序列化,但我有一个例外:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.fifth_llc.siply.main.request.Forbidden$Error` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
     at [Source: (String)"{"errorMessage":{"changePassword":"Old password is incorrect."}}"; line: 1, column: 1]

我也尝试了JsonDeserialize注释,但是如果我想解析为具体类型,似乎可以使用它:

@JsonDeserialize(`as` = MultiError::class)

有什么帮助吗?

自定义解串器方法:

sealed class Error() {
    data class SingleError(val errorMessage: String) : Error()
    data class MultiError(val errorMessage: Map<String, String>) : Error()
}
...
class ErrorDeserializer : StdDeserializer<Error>(Error::class.java) {

    companion object {
        private val MAP_TYPE_REFERENCE = object : TypeReference<Map<String, String>>() {}
    }

    @Throws(IOException::class, JsonProcessingException::class)
    override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): Error {
        val mapper = jp.codec as ObjectMapper
        val node: JsonNode = mapper.readTree(jp)
        val msgNode = node.get("errorMessage")
        if (msgNode.isValueNode) {
            val errorMsg = msgNode.asText()
            return Error.SingleError(errorMsg)
        } else {
            val errorMsgs = mapper.readValue<Map<String, String>>(msgNode.toString(), 
                    MAP_TYPE_REFERENCE)
            return Error.MultiError(errorMsgs)
        }
    }
}

用法:

val mapper = ObjectMapper()
val module = SimpleModule().addDeserializer(Error::class.java, ErrorDeserializer())
mapper.registerModule(module)

val error = mapper.readValue<Error>("json content", Error::class.java)
when (error) {
    is Error.SingleError -> {
        // error.errorMessage
    }
    is Error.MultiError -> {
        // error.errorMessage
    }
}

据我所知,至少我经历过,您不能将抽象类型分配给readValue方法,您必须在readValue parameterizedType及其属性中指定它的具体类型。

jacksonObjectMapper().readValue<Error>(response.body)

在上面的行中,您必须将Error替换为所需的子类型之一,然后在其中必须替换Map bye HashedMap

data class MultiError(val errorMessage: Map<String, String>) : Error()

另一种方法是使用@JsonTypeInfo进入json对象,以通知JackSon将确切的对象类型作为元数据包括到json流中。 此解决方案在服务器和客户端应用程序中都需要代码更正。 请参考以下链接:

https://fasterxml.github.io/jackson-annotations/javadoc/2.4/com/fasterxml/jackson/annotation/JsonTypeInfo.html

问题是杰克逊需要一个JsonCreator或一个空的构造函数。

数据类没有空的构造函数。 如果仍然需要一个空的构造函数,则可以使用Kotlin No-Args插件。

gradle这个:

 dependencies {
 classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion"
     ... 
 }
 ...
 apply plugin: "kotlin-noarg"
 ...
 noArg {
    annotation("com.your.package.NoArgs")
 }

现在,创建自定义注释:

package com.your.package

annotation class NoArgs

现在最简单*的方法是使用一个通用的类型为您的错误信息,并使用自定义的注释:

@NoArgs
data class Error<out T>(val errorMessage: T)

val x = ObjectMapper().readValue<Error<*>>(json1)
val y = ObjectMapper().readValue<Error<*>>(json2)

when(y.errorMessage) {
  is String -> println("I'm a String!")
  is Map<*,*> -> println("I'm a Map")
  else -> println("I'm something else!")
}

如果事先知道,也可以在readValue显式定义类型: readValue<Error<String>>

您还可以创建内部的方法Error在这种逻辑when


*根据您的实际情况,可能会有更优雅或更有用的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM