Search code examples
androidkotlinserializationgson

Kotlin Serialization @SerialName not working for Boolean


I'm using GSON to serialize some platform data. When I use @SerialName to capture platform data with a different naming convention in my app, it works for other types, but not Boolean types. As a simple example, if I have a class like...

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class Person (
    @SerialName("first_name") val firstName: String? = null,
    @SerialName("last_name") val lastName: String? = null,
    val age: Int? = null
)

... everything works fine. The serializer finds first_name, last_name and age in the data and properly set the properties for the Person.

However, when I try to add a Boolean...

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class Person (
    @SerialName("first_name") val firstName: String? = null,
    @SerialName("last_name") val lastName: String? = null,
    val age: Int? = null,
    @SerialName("can_sing") val canSing: Boolean? = null
)

... the serializer does not catch and assign can_sing. It is strange that it works with a String but not a Boolean. Can any explain why I am seeing this behavior? I can work around this (for example, I can do val can_sing: Boolean? = null and it works), but I'm just wonder why @SerialName doesn't seem to work for a Boolean, or if I'm just missing something obvious.


Solution

  • You are mixing the Gson and Kotlin annotation types - Gson uses @SerializedName not @SerialName. I am not sure how your string types even work in that case (maybe something in how you call Gson that isn't included in the question).

    As an example, the first class here (Person) can be serialized with the Kotlin serialization library, the second with Gson:

    Kotlin annotations

    @Serializable
    data class Person (
        @SerialName("first_name") val firstName: String? = null,
        @SerialName("last_name") val lastName: String? = null,
        val age: Int? = null,
        @SerialName("can_sing") val canSing: Boolean? = null
    )
    

    Gson Annotations

    data class PersonGson (
        @SerializedName("first_name") val firstName: String? = null,
        @SerializedName("last_name") val lastName: String? = null,
        val age: Int? = null,
        @SerializedName("can_sing") val canSing: Boolean? = null
    )
    

    Examples

    Running this unit test with the Kotlin serialization library:

    @Test
    fun testJsonKotlin()  {
        val test = Person("hello", "world", 42, false)
        val json = Json.encodeToString(test)
        println(json)
        val t2 = Json.decodeFromString<Person>(json)
        println(t2)
    }
    

    produces the expected output:

    {"first_name":"hello","last_name":"world","age":42,"can_sing":false}
    Person(firstName=hello, lastName=world, age=42, canSing=false)
    

    Doing that with Gson

    @Test
    fun testJsonGsonMixed()  {
        val testp = Person("hello", "world", 42, false)
        val json = Gson().toJson(testp)
        println(json)
        val t2 = Gson().fromJson(json, Person::class.java)
        println(t2)
    }
    

    technically works, but ignores the serialized name annotations (for all the cases, not just the boolean)

    {"firstName":"hello","lastName":"world","age":42,"canSing":false}
    Person(firstName=hello, lastName=world, age=42, canSing=false)
    

    Using the Gson-annotated class with Gson

    @Test
    fun testJsonGson()  {
        val test = PersonGson("hello", "world", 42, false)
        val json = Gson().toJson(test)
        println(json)
        val t2 = Gson().fromJson(json, PersonGson::class.java)
        println(t2)
    }
    

    gives the correct response again

    {"first_name":"hello","last_name":"world","age":42,"can_sing":false}
    PersonGson(firstName=hello, lastName=world, age=42, canSing=false)