JSON枚举反序列化破坏了Kotlin的空安全性

5

我使用Kotlin数据类GSON来反序列化JSON模式,并实现默认值以防止JSON中的空对象。此外,JSON int枚举通过使用@SerializedName注释映射到Kotlin枚举值:

data class Person(@SerializedName("name")
           val name: String = ",
           @SerializedName("age")
           val age: Int = 0,
           @SerializedName("hairColor")
           val hairColor: Color = Color.NONE)

enum class Color{
    @SerializedName("1")
    BROWN,
    @SerializedName("2")
    BLONDE,
    NONE
}

关注枚举反序列化 - 当字段匹配已知枚举或字段完全不存在于JSON中时,这种方法非常有效,此时将实现默认枚举。

但是 - 如果在JSON中收到的枚举映射到我的Kotlin枚举中的已知枚举值,则反序列化的枚举将为null!

{"name":"Joe","age":10,"hairColor":1} ->
Person(name=Joe, age=10, hairColor=BROWN)

{"name":"Jim"} ->
Person(name=Jim, age=0, hairColor=NONE)

{"name":"Jeff", "age":8,"hairColor":3) ->
Person(name=Jane, age=8, hairColor=null)

Gson通过将null赋值给非空类型来欺骗Kotlin的空安全机制。问题在于如何将未知的JSON枚举映射到默认的Kotlin枚举?我的目标是通过简单的实现来保持空安全。
附注:我知道我可以将JSON枚举解析为Int,稍后再进行反序列化,或者使用支持字段和自定义getter,但我喜欢直接解析为Kotlin枚举的优雅和类型安全性。

1
我认为最简单的方法是定义getter方法。请参阅 http://blog.jensdriller.com/simple-deserialization-of-java-enums-using-google-gson-annotations/ 的最后一段。 - beigirad
1
或者,也可以使用自定义的JsonDeserializer(https://github.com/google/gson/blob/master/UserGuide.md#TOC-Writing-a-Deserializer)。 - CommonsWare
1
值得一提的是,如果您可以使用Jackson,它提供了一个Kotlin支持模块 - Salem
1个回答

2
我为Gson编写了一个Kotlin封装,名为Arson,它可以为反序列化的对象添加缺失的默认值。除此之外,它还会检查违反Kotlin非空安全的空值。
快来看看吧:https://github.com/taskbase/arson 在你的项目中使用它:
<dependency>
    <groupId>com.taskbase.arson</groupId>
    <artifactId>arson</artifactId>
    <version>1.0</version>
</dependency>


class ArsonTest {

    @Test
    fun testEnumDeserialization() {
        val json = "{'name':'Jim', 'hairColor':'3'}"

        // Gson deserializes the value to null
        val p1 = Gson().fromJson(json, Person::class.java)
        assertNull(p1.hairColor)

        // The wrapper replaces null with the default value
        val p2 = Arson(gson = Gson()).fromJson(json, Person::class.java)
        assertEquals(Color.NONE, p2.hairColor)
    }
}

data class Person(
    val name: String = "",
    val age: Int = 0,
    val hairColor: Color = Color.NONE
)

enum class Color {
    @SerializedName("1")
    BROWN,
    @SerializedName("2")
    BLONDE,
    NONE
}

我也曾因Gson不支持Kotlin而苦恼。Gson是一个Java库,无法理解Kotlin的类型系统。我测试了几个其他的JSON库,但都不够好用。因此,我编写了一个函数,使用Kotlin反射库创建对象的深拷贝,并添加缺失的默认值。

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