gpt4 book ai didi

generics - 使用 jackson-module-kotlin 反序列化通用类

转载 作者:行者123 更新时间:2023-12-02 20:11:33 26 4
gpt4 key购买 nike

我正在尝试使用 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 的答案中的代码有效。事实证明,与我的代码的主要区别在于我在测试方法中定义了数据类。将它们移到顶层可以解决问题。将数据类放在函数中一开始并不是一个好主意。

最佳答案

使用 Jackson 的 2.9.6 以及 Jackson 的主分支 2.9.8 以及当前的 Jackson-Kotlin 模块,我添加了这个通过的测试用例对于您的代码版本和代码的更简洁的惯用版本。

请注意,我也更新了 stringify 方法,使其更加地道,但这不会影响测试。

您对 readValue 的调用也比必要的更复杂。变化:

val parsed = objectMapper.readValue<Outer<Inner>>(serialized, objectMapper.typeFactory.constructParametricType(Outer::class.java, Inner::class.java))

简单地说:

val parsed = objectMapper.readValue<Outer<Inner>>(serialized)    

这是完整的通过测试:

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:
// 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))
assertEquals(42, parsed.inner.meaningOfLife)
}
}

enter image description here

关于generics - 使用 jackson-module-kotlin 反序列化通用类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53499407/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com