Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson Deserialization with Kotlin, Initializer block not called

Tags:

gson

kotlin

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?


like image 327
Lukas Lechner Avatar asked Oct 10 '16 16:10

Lukas Lechner


2 Answers

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
like image 52
hotkey Avatar answered Nov 03 '22 12:11

hotkey


I had a similar issue with Gson. My data class had some serialized paraments and few non serialized. And I wanted to make my non serialized parameters with default values. The simplest solution I found is to make my non serialized parameters optional. Here is my workaround:

data class MyClass(
@SerializedName("name") val name: String // All Serialize goes here
) { // Non serialized
    var status: String?
        get {
            return if (field == null) {
                "Nice status"
            } else {
                field
            }
        }
}
like image 2
Kirk_hehe Avatar answered Nov 03 '22 13:11

Kirk_hehe