代码之家  ›  专栏  ›  技术社区  ›  Jilles van Gurp

使用Jackson模块Kotlin反序列化泛型类

  •  0
  • Jilles van Gurp  · 技术社区  · 7 年前

    我正在尝试使用杰克逊-科特林整合。大多数情况下它工作得很好,但是我在反序列化泛型类型方面遇到了困难。我试图使这个问题的答案: 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给出的答案中的代码有效。结果发现,与我的代码的关键区别在于,我在测试方法中定义了数据类。把它们移到顶层可以解决问题。首先在函数中放入一个数据类是一个坏主意。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Jayson Minard    7 年前

    使用Jackson的 2.9.6->code>以及Jackson的master branch 2.9.8->code>以及当前的Jackson Kotlin模块,我添加了这个测试用例,它同时通过了您的代码版本和更清晰的代码惯用版本。

    请注意,我更新了 stringify method,使其更加惯用,但这不会影响测试。

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

    代码> ValePrace= ObjutMaPr.Read Value& lt;Eng≫;(序列化,ObjistMaPl.TyrPrime.Cu构tMul形形体类型(Elut::Clase.java,In::Clasy.java)) < /代码>

    简单地说:

    val parsed=objectmapper.readValue<outer<inner>>(序列化)
    

    这是完全通过的测试:

    class teststackoverflow53499407{
    内部数据类(val-intermeoflife:int)
    数据类外部<T>(VAL内部:T)
    
    fun<t>objectmapper.stringify(value:t,pretty:boolean=false):string{
    StringWriter()。使用Writer->
    如果(漂亮){
    WriterWithDefaultPrettyprinter().WriteValue(Writer,Value)
    }否则{
    WriteValue(写入程序,值)
    }
    返回writer.toString()。
    }
    }
    
    @测试
    fun test53499407_cleantest()。{
    VAL外部=外部(内部(42))
    val objectmapper=jacksonObjectmapper()。
    
    val serialized=objectmapper.stringify(外部,真)
    println(序列化)
    
    val parsed=objectmapper.readValue<外部<内部>>(序列化)
    断言等于(42,已解析。内部。生命意义)
    }
    
    @测试
    fun test53499407_惯用\u tweek()。{
    VAL外部=外部(内部(42))
    
    val jsonFactory=jsonFactory()。
    jsonFactory.configure(jsonGenerator.feature.auto_close_target,false)
    jsonFactory.configure(jsonParser.feature.ignore_undefined,true)
    
    val objectmapper=对象映射器(jsonFactory)
    objectmapper.find和registermodules()。
    objectmapper.propertyNamingStrategy=propertyNamingStrategy.snakeSategy()。
    objectmapper.setserializationInclusion(jsoninclude.include.non-null)
    
    val serialized=objectmapper.stringify(外部,真)
    println(序列化)
    
    //此行改为惯用
    val parsed=objectmapper.readValue<外部<内部>>(序列化)
    断言等于(42,已解析。内部。生命意义)
    }
    
    @测试
    fun test53499407_as_written_in_stackoverflow()中{
    VAL外部=外部(内部(42))
    
    val jsonFactory=jsonFactory()。
    jsonFactory.configure(jsonGenerator.feature.auto_close_target,false)
    jsonFactory.configure(jsonParser.feature.ignore_undefined,true)
    
    val objectmapper=对象映射器(jsonFactory)
    objectmapper.find和registermodules()。
    objectmapper.propertyNamingStrategy=propertyNamingStrategy.snakeSategy()。
    objectmapper.setserializationInclusion(jsoninclude.include.non-null)
    
    val serialized=objectmapper.stringify(外部,真)
    println(序列化)
    
    //反序列化无法使用:
    //https://stackoverflow.com/questions/11664894/jackson-deserialize-using-generic-class
    ValPals= ObjutMaPr.Read Value& lt;Eng≫;(序列化,ObjistMaPl.TyrPrime.Cu构tMul形形体类型(Elut::Clase.java,In::Clasy.java))
    断言等于(42,已解析。内部。生命意义)
    }
    }
    

    这个测试用例通过了您的代码版本和更清晰的代码惯用版本。

    注意我更新了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