使用 gson 排除 Kotlin 数据类属性/字段的序列化/反序列化

16

我正在尝试使用gson从反序列化中排除一个Kotlin属性。我尝试过不同的方法,例如使用@Transient注释属性或创建自定义注释策略(当然,在gson构建器中指定该策略),但似乎什么都不起作用,因为该属性始终为空,而不是使用我初始化该属性的值。

我还没有尝试使用@Expose注释,但我不想给其他字段加上@Expose注释

请问,如何使用gson + Kotlin实现这一目标?

5个回答

24

@Transient 对我有用。

@Transient lateinit var bar: SomeCustomType
根据 @Transient 的定义:
标记带有该注解的属性的 JVM 后备字段为 transient,这意味着它不是对象默认序列化形式的一部分。

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

这不需要我使用@Expose标记其他我想要反序列化的字段吗?因为我同样想避免这种情况。 - idrisadetunmbi
1
这取决于您如何创建Gson Builder。例如:gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation() .create() - 将排除其他字段。 - Sergei Rybalkin
如果我正在初始化bar属性怎么办?这正是问题所在。当我初始化该属性时,它会返回null。 - idrisadetunmbi

2

目前我还没有找到更加优雅的解决方案,但是暂时我已经给属性取了一个不同的名称,并且从类的默认构造函数中移除了它。

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

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

然后我可以在自定义反序列化器中分配lNameObj

1
你可以使用注释来实现相同的效果。示例
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)
    }
}

单元测试

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

我从baeldung了解了关于注释的内容。

0

我试图排除 displayedName 和 我在这里尝试了解决方案,但对我有效的是

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)

在上传请求之前,我将displayedName设置为null,以便它不会被序列化。

有没有任何方法可以在运行时添加它? - CHAN
什么是在运行时添加的意思? - Mahmoud Mabrok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接