Jil*_*urp 3 generics jackson kotlin
我正在尝试使用 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:
// /sf/ask/816542611/
val parsed = objectMapper.readValue<Outer<Inner>>(serialized, objectMapper.typeFactory.constructParametricType(Outer::class.java,Inner::class.java))
Run Code Online (Sandbox Code Playgroud)
这会引发异常:
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
Run Code Online (Sandbox Code Playgroud)
显然 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)
}
Run Code Online (Sandbox Code Playgroud)
更新@jayson-minard 答案中的代码有效。事实证明,我的代码的主要区别在于我在测试方法中定义了数据类。将它们移到顶层可以解决问题。首先,将数据类放入函数中并不是一个好主意。
使用2.9.6Jackson,然后使用2.9.8Jackson 的主分支以及当前的 Jackson-Kotlin 模块,我添加了这个测试用例,它既适用于您的代码版本,也适用于更清晰的惯用版本的代码。
请注意,我stringify也更新了该方法以使其更加惯用,但这不会影响测试。
您的呼叫readValue也比必要的更加复杂。改变:
val parsed = objectMapper.readValue<Outer<Inner>>(serialized, objectMapper.typeFactory.constructParametricType(Outer::class.java, Inner::class.java))
Run Code Online (Sandbox Code Playgroud)
简单地说:
val parsed = objectMapper.readValue<Outer<Inner>>(serialized)
Run Code Online (Sandbox Code Playgroud)
这是完整通过的测试:
class TestStackOverflow53499407 {
data class Inner(val meaningOfLife: Int)
data class Outer<T>(val inner: T)
fun <T> ObjectMapper.stringify(value: T, pretty: Boolean = false): String {
StringWriter().use { writer ->
if (pretty) {
writerWithDefaultPrettyPrinter().writeValue(writer, value)
} else {
writeValue(writer, value)
}
return writer.toString()
}
}
@Test
fun test53499407_cleanTest() {
val outer = Outer(Inner(42))
val objectMapper = jacksonObjectMapper()
val serialized = objectMapper.stringify(outer, true)
println(serialized)
val parsed = objectMapper.readValue<Outer<Inner>>(serialized)
assertEquals(42, parsed.inner.meaningOfLife)
}
@Test
fun test53499407_idiomatic_tweek() {
val outer = Outer(Inner(42))
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)
val serialized = objectMapper.stringify(outer, true)
println(serialized)
// This line changed to be idiomatic
val parsed = objectMapper.readValue<Outer<Inner>>(serialized)
assertEquals(42, parsed.inner.meaningOfLife)
}
@Test
fun test53499407_as_written_in_stackoverflow() {
val outer = Outer(Inner(42))
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)
val serialized = objectMapper.stringify(outer, true)
println(serialized)
// deserializing does not work using:
// /sf/ask/816542611/
val parsed = objectMapper.readValue<Outer<Inner>>(serialized, objectMapper.typeFactory.constructParametricType(Outer::class.java, Inner::class.java))
assertEquals(42, parsed.inner.meaningOfLife)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7374 次 |
| 最近记录: |