使用Kotlin的Gson反序列化,未调用初始化块 [英] Gson Deserialization with Kotlin, Initializer block not called

查看:608
本文介绍了使用Kotlin的Gson反序列化,未调用初始化块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我创建我的对象时,我的初始化块工作得很好

  ObjectToDeserialize(var someString:String =) :可序列化{
init {
someString + =初始化
}
}

这样:

  @Test 
fun createObject_checkIfInitialized(){
assertEquals(someString initialized,ObjectToDeserialize(someString)。someString)
}

但是,当我用Gson反序列化对象时,初始化块不会被执行:

  @Test 
fun deserializeObject_checkIfInitialized ){
val someJson:String ={\someString \:\someString\}
val jsonObject = Gson()。fromJson(someJson,ObjectToDeserialize :: class。 java)

assertEquals(someString initialized,jsonObject.someString)

//预计:someString初始化
//实际值:s omeString
}

我认为gson以与执行不同的方式创建对象主要构造函数。是否有可能有类似的初始化块?





解决方案

Gson是打算用于纯Java,并且不会正确解释Kotlin主构造函数。



如果有一个默认的无参数构造函数,它会被调用(在您的情况下,是1:主要构造函数的默认值用于 someString ),其他Gson根本不会调用构造函数

<然后Gson设置属性值(也绕过了Kotlin属性的实际设置者并直接设置字段,这有时会导致意外的行为。)






或者,您可以使用 jackson-module-kotlin ,它应该适合你的情况(它理解Kotlin的主要构造函数,使用Kotlin设置器并支持默认参数值,因为 2.8.x )。

这个例子比较了jackson-module-kotlin和 Kotson 的行为:

  class SomeClass(val id:Int = -1){
init {println(init $ id)}
}

class SomeClassNoDefault(val id:Int){
init {println(init $ id)}
}

fun main(args:Array< String> ;){
val mapper = jacksonObjectMapper()
val gson = Gson()
$ b $ val val idDefault ={}
val id123 ={\ id \:123}

println(jackson-module-kotlin,{}:)
val o1 = mapper.readValue< SomeClass>(idDefault)
println(建设后:$ {o1.id} \\\


println(jackson-module-kotlin,{\id \= 123}:)
val o2 = mapper.readValue< SomeC lass>(id123)
println(after construction:$ {o2.id} \\\


println(kotson,{}:)
val o3 =gson.fromJson< SomeClass>(idDefault)
println(after construction:$ {o3.id} \\\


println(kotson,{\id \\ = 123}:)
val o4 = gson.fromJson< SomeClass>(id123)
println(after construction:$ {o4.id} \)

println(--- \\\


println(jackson-module-kotlin,无默认值,{\id \= 123}:)
val o5 = mapper.readValue< SomeClassNoDefault>(id123)
println(after construction:$ {o5.id} \\\


println(kotson,no默认值:{\id\= 123}:)
val o6 = gson.fromJson< SomeClassNoDefault>(id123)
println(after construction:$ {o6.id} \\ \\ n)
}

输出是


  jackson-module-kotlin,{}:
init -1 $施工后的b $ b:-1

施工后:123
$ b jackson-module-kotlin,{id= 123}:
init 123
$ b kotson,{}:
init -1
施工后:-1

kotson,{id= 123}:
init -1 $ b建设后的$ b:123

---

jackson-module-kotlin,无默认值,{id= 123}:
init 123 $施工后的b $ b:123

kotson,无默认值,{id= 123}:施工后的
:123
pre>


My initializer block is working perfectly fine when I create my Object

class ObjectToDeserialize(var someString: String = "") : Serializable {
    init{
        someString += " initialized"
    }
}

this way:

@Test
fun createObject_checkIfInitialized() {
      assertEquals("someString initialized",ObjectToDeserialize("someString").someString)
}

But when I deserialize the object with Gson, the initializer block does not get executed:

@Test
fun deserializeObject_checkIfInitialized(){
    val someJson: String = "{\"someString\":\"someString\" }"
    val jsonObject = Gson().fromJson(someJson, ObjectToDeserialize::class.java)

    assertEquals("someString initialized",jsonObject.someString)

    // Expected :someString initialized
    // Actual   :someString
}

I think that gson is creating the object in a different way than by executing the primary constructor. Is it nevertheless possible to have something similar like initializer blocks?


解决方案

Gson is intended for usage with pure Java and doesn't interpret Kotlin primary constructors properly.

If there is a default no-argument constructor, it is called (in your case, there is one: the primary constructor with default value "" for someString), otherwise Gson calls no constructor at all.

Then Gson sets the property values (also, it bypasses actual setters of Kotlin properties and sets the fields directly, which may sometimes lead to unexpected behavior).


As an alternative, you can use jackson-module-kotlin, it should work good in your case (it understands Kotlin primary constructors, uses Kotlin setters and supports default parameter values as well since 2.8.x).

This example compares jackson-module-kotlin and Kotson behavior:

class SomeClass(val id: Int = -1) {
    init { println("init $id") }
}

class SomeClassNoDefault(val id: Int) {
    init { println("init $id") }
}

fun main(args: Array<String>) {
    val mapper = jacksonObjectMapper()
    val gson = Gson()

    val idDefault = "{}"
    val id123 = "{\"id\": 123 }"

    println("jackson-module-kotlin, { }:")
    val o1 = mapper.readValue<SomeClass>(idDefault)
    println("after construction: ${o1.id}\n")

    println("jackson-module-kotlin, { \"id\" = 123 }:")
    val o2 = mapper.readValue<SomeClass>(id123)
    println("after construction: ${o2.id}\n")

    println("kotson, { }:")
    val o3 = gson.fromJson<SomeClass>(idDefault)
    println("after construction: ${o3.id}\n")

    println("kotson, { \"id\" = 123 }:")
    val o4 = gson.fromJson<SomeClass>(id123)
    println("after construction: ${o4.id}\n")

    println("---\n")

    println("jackson-module-kotlin, no default value, { \"id\" = 123 }:")
    val o5 = mapper.readValue<SomeClassNoDefault>(id123)
    println("after construction: ${o5.id}\n")

    println("kotson, no default value, { \"id\" = 123 }:")
    val o6 = gson.fromJson<SomeClassNoDefault>(id123)
    println("after construction: ${o6.id}\n")
}

The output is

jackson-module-kotlin, { }:
init -1
after construction: -1

jackson-module-kotlin, { "id" = 123 }:
init 123
after construction: 123

kotson, { }:
init -1
after construction: -1

kotson, { "id" = 123 }:
init -1
after construction: 123

---

jackson-module-kotlin, no default value, { "id" = 123 }:
init 123
after construction: 123

kotson, no default value, { "id" = 123 }:
after construction: 123

这篇关于使用Kotlin的Gson反序列化,未调用初始化块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆