可选+可空值的Gson反序列化

rqmkfv5c  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(245)

我试图解决的问题是完美地描述了以下文本从this link
举一个具体的例子来说明这在什么情况下有用,考虑一个支持对象部分更新的API。使用这个API,JSON对象将被用来传递某个长期对象的补丁。任何包含的属性都指定该对象的相应值应该被更新,而任何省略的属性的值应该保持不变。如果该对象的任何属性都是可空的,则为属性发送的空值与丢失的属性根本不同,因此必须区分这些情况。
那篇文章提出了一个解决方案,但是使用了kotlinx.serialization库,但是,我现在必须使用gson库。
因此,我试图实现我自己的解决方案,因为我没有找到任何可以适合我的用例(请让我知道,如果有)。

data class MyObject(
    val fieldOne: OptionalProperty<String> = OptionalProperty.NotPresent,
    val fieldTwo: OptionalProperty<String?> = OptionalProperty.NotPresent,
    val fieldThree: OptionalProperty<Int> = OptionalProperty.NotPresent
)

fun main() {
    val gson = GsonBuilder()
        .registerTypeHierarchyAdapter(OptionalProperty::class.java, OptionalPropertyDeserializer())
        .create()
    val json1 = """{
            "fieldOne": "some string",
            "fieldTwo": "another string",
            "fieldThree": 18
        }
        """
    println("json1 result object: ${gson.fromJson(json1, MyObject::class.java)}")
    val json2 = """{
            "fieldOne": "some string",
            "fieldThree": 18
        }
        """
    println("json2 result object: ${gson.fromJson(json2, MyObject::class.java)}")
    val json3 = """{
            "fieldOne": "some string",
            "fieldTwo": null,
            "fieldThree": 18
        }
        """
    println("json3 result object: ${gson.fromJson(json3, MyObject::class.java)}")

}

sealed class OptionalProperty<out T> {

    object NotPresent : OptionalProperty<Nothing>()

    data class Present<T>(val value: T) : OptionalProperty<T>()
}

class OptionalPropertyDeserializer : JsonDeserializer<OptionalProperty<*>> {

    private val gson: Gson = Gson()

    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): OptionalProperty<*> {
        println("Inside OptionalPropertyDeserializer.deserialize json:$json")
        return when {
            // Is it a JsonObject? Bingo!
            json?.isJsonObject == true ||
                    json?.isJsonPrimitive == true-> {

                // Let's try to extract the type in order
                // to deserialize this object
                val parameterizedType = typeOfT as ParameterizedType

                // Returns an Present with the value deserialized
                return OptionalProperty.Present(
                    context?.deserialize<Any>(
                        json,
                        parameterizedType.actualTypeArguments[0]
                    )!!
                )
            }
            // Wow, is it an array of objects?
            json?.isJsonArray == true -> {

                // First, let's try to get the array type
                val parameterizedType = typeOfT as ParameterizedType

                // check if the array contains a generic type too,
                // for example, List<Result<T, E>>
                if (parameterizedType.actualTypeArguments[0] is WildcardType) {

                    // In case of yes, let's try to get the type from the
                    // wildcard type (*)
                    val internalListType = (parameterizedType.actualTypeArguments[0] as WildcardType).upperBounds[0] as ParameterizedType

                    // Deserialize the array with the base type Any
                    // It will give us an array full of linkedTreeMaps (the json)
                    val arr = context?.deserialize<Any>(json, parameterizedType.actualTypeArguments[0]) as ArrayList<*>

                    // Iterate the array and
                    // this time, try to deserialize each member with the discovered
                    // wildcard type and create new array with these values
                    val result = arr.map { linkedTreeMap ->
                        val jsonElement = gson.toJsonTree(linkedTreeMap as LinkedTreeMap<*, *>).asJsonObject
                        return@map context.deserialize<Any>(jsonElement, internalListType.actualTypeArguments[0])
                    }

                    // Return the result inside the Ok state
                    return OptionalProperty.Present(result)
                } else {
                    // Fortunately it is a simple list, like Array<String>
                    // Just get the type as with a JsonObject and return an Ok
                    return OptionalProperty.Present(
                        context?.deserialize<Any>(
                            json,
                            parameterizedType.actualTypeArguments[0]
                        )!!
                    )
                }
            }
            // It is not a JsonObject or JsonArray
            // Let's returns the default state NotPresent.
            else -> OptionalProperty.NotPresent
        }
    }

}

我从here中获得了定制反序列化程序的大部分代码。
这是我运行main函数时的输出:

Inside OptionalPropertyDeserializer.deserialize json:"some string"
Inside OptionalPropertyDeserializer.deserialize json:"another string"
Inside OptionalPropertyDeserializer.deserialize json:18
json1 result object: MyObject(fieldOne=Present(value=some string), fieldTwo=Present(value=another string), fieldThree=Present(value=18))
Inside OptionalPropertyDeserializer.deserialize json:"some string"
Inside OptionalPropertyDeserializer.deserialize json:18
json2 result object: MyObject(fieldOne=Present(value=some string), fieldTwo=my.package.OptionalProperty$NotPresent@573fd745, fieldThree=Present(value=18))
Inside OptionalPropertyDeserializer.deserialize json:"some string"
Inside OptionalPropertyDeserializer.deserialize json:18
json3 result object: MyObject(fieldOne=Present(value=some string), fieldTwo=null, fieldThree=Present(value=18))

我正在测试fieldTwo的不同选项,它几乎完全正常工作,除了第3rd json,我希望fieldTwo应该是fieldTwo=Present(value=null)而不是fieldTwo=null
我看到在这种情况下,甚至没有为fieldTwo调用自定义反序列化程序。
有人能看出我在这里错过了什么吗?任何提示都将非常感激!

ztigrdn8

ztigrdn81#

我最终放弃了gson,搬到了莫希。
我基于此注解中提供的解决方案实现了此行为。

相关问题