Exclude a Kotlin data class property/field from serialization/deserialization using gson

末鹿安然 提交于 2020-12-10 02:35:06

问题


I am trying to exclude a Kotlin property from deserialization using gson. I have tried different methods from annotating the property with @Transient to creating a custom annotation strategy (specifying the strategy in the gson builder of course), but nothing seems to be working, as the property keeps getting null instead of the value I initialized the property with.

I have not tried using the @Expose annotation, but I do not want to annotate other fields with @Expose

Please, how can I achieve this please using gson + Kotlin?


回答1:


@Transient worked for me.

@Transient lateinit var bar: SomeCustomType

Per @Transient definition:

Marks the JVM backing field of the annotated property as transient, meaning that it is not part of the default serialized form of the object.




回答2:


data class Foo (
    @Expose(deserialize = false) val bar: Bar
)



回答3:


I have yet to find a more elegant solution, but for now, I've given the property a different name and removed it from the class's default constructor.

{
    "fName": "Bilbo"
    "lName": "Baggins"
}

data class Person(val fName: String) {
    lateinit var lNameObj: String
}

Then I can assign lNameObj in my custom deserializer.




回答4:


You can use annotation to achieve the same. Example

package com.xently.utils

import com.google.gson.*
import com.xently.utils.Exclude.During.*
import org.intellij.lang.annotations.Language
import java.text.DateFormat

@Retention(value = AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
/**
 * Excludes field(s) from json serialization and/or deserialization depending on [during]
 * @param during When to exclude field from serialization and/or deserialization
 */
annotation class Exclude(val during: During = BOTH) {
    /**
     * @see SERIALIZATION Exclude field ONLY from json serialization
     * @see DESERIALIZATION Exclude field ONLY from json deserialization
     * @see BOTH Exclude field from json serialization and deserialization
     */
    enum class During {
        /**
         * Exclude field ONLY from json serialization
         */
        SERIALIZATION,

        /**
         * Exclude field ONLY from json deserialization
         */
        DESERIALIZATION,

        /**
         * Exclude field from json serialization and deserialization
         */
        BOTH
    }
}

interface IData<T> {
    fun fromJson(@Language("JSON") json: String?): T?

    fun fromMap(map: Map<String, Any?>): T? = fromJson(JSON_CONVERTER.toJson(map))
}

inline fun <reified T> fromJson(json: String?): T? = if (json.isNullOrBlank()) null else try {
    JSON_CONVERTER.fromJson(json, T::class.java)
} catch (ex: Exception) {
    null
}

private fun getExclusionStrategy(during: Exclude.During = Exclude.During.BOTH): ExclusionStrategy {
    return object : ExclusionStrategy {
        override fun shouldSkipClass(clazz: Class<*>?): Boolean {
            return false
        }

        override fun shouldSkipField(f: FieldAttributes?): Boolean {
            return if (f == null) true else {
                val annotation = f.getAnnotation(Exclude::class.java)
                if (annotation == null) {
                    false
                } else {
                    annotation.during == during
                }
            }
        }
    }
}

val JSON_CONVERTER: Gson = GsonBuilder()
    .enableComplexMapKeySerialization()
    .addSerializationExclusionStrategy(getExclusionStrategy(SERIALIZATION))
    .addDeserializationExclusionStrategy(getExclusionStrategy(DESERIALIZATION))
    .setExclusionStrategies(getExclusionStrategy())
    .serializeNulls()
    .setDateFormat(DateFormat.LONG)
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create()

data class GsonSerializable(
    val name: String?,
    val age: Int = 0,
    val sex: Sex = Sex.MALE,
    @Exclude
    val ignore: String? = "ISD",
    @Exclude(DESERIALIZATION)
    val ignoreDes: String? = "ID",
    @Exclude(SERIALIZATION)
    val ignoreSer: String? = "IS"
) {
    enum class Sex {
        MALE,
        FEMALE
    }

    @Exclude(SERIALIZATION)
            /**
             * Without annotation it'd be serialized!
             */
    val increaseAgeBy: (increment: Int) -> Int = {
        age + it
    }

    override fun toString(): String = JSON_CONVERTER.toJson(this)

    companion object : IData<GsonSerializable> {
        override fun fromJson(json: String?): GsonSerializable? = com.xently.utils.fromJson(json)
    }
}

Unit test

package com.xently.utils

import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.isEmptyOrNullString
import org.junit.Test

class GsonSerializableTest {
    @Test
    fun excludeAnnotationWorksCorrectly() {
        val test1 = GsonSerializable("My name", 23, GsonSerializable.Sex.FEMALE)

        val json1 = test1.toString()
        val testDesJson1 =
            GsonSerializable.fromJson("""{"name":"My name","age":23,"sex":"FEMALE","ignore_des":"ID","ignore_ser":"IS"}""")

        assertThat(test1.increaseAgeBy.invoke(2), equalTo(25))
        assertThat(
            json1,
            equalTo("""{"name":"My name","age":23,"sex":"FEMALE","ignore_des":"ID"}""")
        )
        if (testDesJson1 != null) {
            assertThat(testDesJson1.ignoreDes, isEmptyOrNullString())
            assertThat(testDesJson1.ignoreSer, equalTo("IS"))
        }
    }
}

I learnt about the annotation stuff from baeldung




回答5:


I was trying to exclude displayedName and I have tried solutions here but what worked with me is

data class Social(
val id: Int? = null,
var social_media_url: String? = null,
val gym_id: Int? = null,
val social_media: String? = null,
val displayedName:String? = null)

before upload request i set displayedName to null so it will not serialized



来源:https://stackoverflow.com/questions/53425600/exclude-a-kotlin-data-class-property-field-from-serialization-deserialization-us

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!