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

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

问题描述

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

class ObjectToDeserialize(var someString: String = "") : Serializable {在里面{someString += "已初始化"}}

这样:

@Test有趣的 createObject_checkIfInitialized() {assertEquals("someString 初始化",ObjectToDeserialize("someString").someString)}

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

@Test有趣的 deserializeObject_checkIfInitialized(){val someJson: String = "{"someString":"someString" }"val jsonObject = Gson().fromJson(someJson, ObjectToDeserialize::class.java)assertEquals("someString 初始化",jsonObject.someString)//预期 :someString 已初始化//实际 :someString}

我认为 gson 以与执行主构造函数不同的方式创建对象.有没有可能有类似初始化程序块的东西?

<小时>

解决方案

Gson 旨在与纯 Java 一起使用,不能正确解释 Kotlin 主构造函数.

如果有一个默认的无参数构造函数,它会被调用(在你的例子中,有一个:主构造函数,其默认值为 "" for someString), 否则 Gson 根本不调用构造函数.

然后 Gson 设置属性值(此外,它绕过 Kotlin 属性的实际设置器并直接设置字段,这有时可能会导致意外行为).

<小时>

作为替代,您可以使用 jackson-module-kotlin,它应该可以工作你的情况很好(它理解 Kotlin 主构造函数,使用 Kotlin setter 并支持默认参数值以及自 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") }}有趣的主(参数:数组<字符串>){val mapper = jacksonObjectMapper()val gson = Gson()val idDefault = "{}"val id123 = "{"id": 123 }"println("jackson-module-kotlin, { }:")val o1 = mapper.readValue(idDefault)println("构造后:${o1.id}
")println("jackson-module-kotlin, { "id" = 123 }:")val o2 = mapper.readValue(id123)println("构造后:${o2.id}
")println("kotson, { }:")val o3 = gson.fromJson(idDefault)println("构造后:${o3.id}
")println("kotson, { "id" = 123 }:")val o4 = gson.fromJson(id123)println("构造后:${o4.id}
")println("---
")println("jackson-module-kotlin, 无默认值,{ "id" = 123 }:")val o5 = mapper.readValue(id123)println("构造后:${o5.id}
")println("kotson,无默认值,{ "id" = 123 }:")val o6 = gson.fromJson(id123)println("构造后:${o6.id}
")}

输出是

<块引用>

jackson-module-kotlin, { }:初始化 -1施工后:-1杰克逊模块科特林,{id"= 123}:初始化 123施工后:123科特森,{}:初始化 -1施工后:-1科特森,{id"= 123}:初始化 -1施工后:123---jackson-module-kotlin,无默认值,{id"=123}:初始化 123施工后:123kotson, 无默认值, { "id" = 123 }:施工后:123

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}
")

    println("jackson-module-kotlin, { "id" = 123 }:")
    val o2 = mapper.readValue<SomeClass>(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, no default value, { "id" = 123 }:")
    val o5 = mapper.readValue<SomeClassNoDefault>(id123)
    println("after construction: ${o5.id}
")

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

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天全站免登陆