【发布时间】:2019-04-29 04:16:24
【问题描述】:
我正在尝试使用 jackson-kotlin 集成。大多数情况下它工作得很好,但我在反序列化泛型类型时遇到了麻烦。我试图调整这个问题的答案:Jackson - Deserialize using generic class
// create an object mapper
val jsonFactory = JsonFactory()
jsonFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false)
jsonFactory.configure(JsonParser.Feature.IGNORE_UNDEFINED, true)
val objectMapper = ObjectMapper(jsonFactory)
objectMapper.findAndRegisterModules()
objectMapper.propertyNamingStrategy = PropertyNamingStrategy.SnakeCaseStrategy()
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
// simple generic type
data class Inner(val meaningOfLife: Int)
data class Outer<T>(val inner: T)
val outer = Outer(Inner(42))
val serialized = objectMapper.stringify(outer, true)
println(serialized)
// deserializing does not work using:
// https://stackoverflow.com/questions/11664894/jackson-deserialize-using-generic-class
val parsed = objectMapper.readValue<Outer<Inner>>(serialized, objectMapper.typeFactory.constructParametricType(Outer::class.java,Inner::class.java))
这会引发异常:
com.fasterxml.jackson.databind.JsonMappingException: Cannot deserialize Class io.inbot.common.ObjectMapperTest$should handle generics$Outer (of type local/anonymous) as a Bean
at [Source: (String)"{
"inner" : {
"meaning_of_life" : 42
}
}"; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:306)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:268)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:477)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4190)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4009)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3042)
at io.inbot.common.ObjectMapperTest.should handle generics(ObjectMapperTest.kt:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:580)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:716)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:988)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
at org.testng.SuiteRunner.run(SuiteRunner.java:364)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
at org.testng.TestNG.runSuites(TestNG.java:1049)
at org.testng.TestNG.run(TestNG.java:1017)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Caused by: java.lang.IllegalArgumentException: Cannot deserialize Class io.inbot.common.ObjectMapperTest$should handle generics$Outer (of type local/anonymous) as a Bean
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.isPotentialBeanType(BeanDeserializerFactory.java:877)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:131)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:411)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
... 31 more
显然,kotlin 的 jackson 插件不能处理这个问题。是否有解决方法或其他方法?
顺便说一句。 stringify 函数是我为 ObjectMapper 添加的一个简单扩展函数,负责处理样板代码:
/**
* Serializes [value] to a string. Pretty prints if [pretty] is set.
*/
fun <T> ObjectMapper.stringify(value: T, pretty: Boolean = false): String {
val bos = ByteArrayOutputStream()
val writer = OutputStreamWriter(bos, StandardCharsets.UTF_8)
if(pretty) {
writerWithDefaultPrettyPrinter().writeValue(writer,value)
} else {
writeValue(writer, value)
}
writer.flush()
bos.flush()
return bos.toByteArray().toString(StandardCharsets.UTF_8)
}
更新@jayson-minard 的答案中的代码有效。事实证明,与我的代码的主要区别在于我在测试方法中定义了数据类。将它们移到顶层可以解决问题。一开始就将数据类放入函数中是个坏主意。
【问题讨论】:
-
你有没有在没有你的类型工厂调用的情况下尝试过这个,让
readValue扩展函数只是推断出具体的类型? -
您使用的是什么版本的 Jackson?您使用的是什么版本的 Jackson-Kotlin 模块?什么版本的 Kotlin?
-
2.9.7 两者,类型推断在这里不起作用,因为杰克逊无法知道类型是什么。还有 Java 8、kotlin 1.3.10