rest - Kotlin - Ktor - 如何处理 PATCH 调用中的可选 API 资源字段?

标签 rest kotlin option-type ktor

使用 Ktor(和 Kotlin)实现 REST API 时,它支持 Kotlin 的可选字段处理。这适用于 POST 和 GET,但是 PATCH(更新)呢?

例如,您有以下资源:

@Serializable
data class MyAddress(
    var line1: String? = null,
    var line2: String? = null,
    var city: String? = null,
    var postal_code: String? = null,
    var state: String? = null,
    var country: String? = null
)

因此所有 MyAddress 字段都是可选的(具有默认值)。

当您使用 POST 创建地址时:

   "line1" : "line1",
   "country" : "XX"

并且您想要使用补丁删除该国家/地区:

   "country" : null

资源的最终结果应该是:

   "line1" : "line1"

但是如何通过解析 PATCH 请求的 json 来确定这一点呢?因为据我所知,没有办法确定它是否是 null默认情况下,或已提交。

此外,默认 null MyAddress 的值是必需的,否则解析将无法进行。

代码示例:

import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json

@kotlinx.serialization.Serializable
data class MyAddress(
    var line1: String? = null,
    var line2: String? = null,
    var city: String? = null,
    var postal_code: String? = null,
    var state: String? = null,
    var country: String? = null
)

fun main() {
    val jsonStringPOST = "{\"line1\":\"street\",\"country\":\"GB\"}"
    println("JSON string is: $jsonStringPOST")

    val myAddressPost = Json.decodeFromString<MyAddress>(jsonStringPOST)
    println("MyAddress object: $myAddressPost")

    val jsonStringPATCH = "{\"country\":null}"
    println("JSON string is: $jsonStringPATCH")

    val myAddressPatch = Json.decodeFromString<MyAddress>(jsonStringPATCH)
    println("MyAddress object: $myAddressPatch")
}

我尝试添加Optional<String>?同样,但它提示缺少 Optional 的序列化,并且最好我不想将所有数据设为 var 的可选值。

注意:我正在寻找一种更加结构化的解决方案,该解决方案也适用于 API 中的所有其他资源(10 多个类)。

最佳答案

第二个解决方案,基于 Aleksei 的示例:


@Serializable
data class Address2(val line1: OptionalValue<String> = Undefined, val country: OptionalValue<String> = Undefined)

@Serializable(with = OptionalValueSerializer::class)
sealed interface OptionalValue<out T>
object Undefined: OptionalValue<Nothing> {
    override fun toString(): String = "Undefined"
}
object Absent: OptionalValue<Nothing> {
    override fun toString(): String = "Absent"
}
class WithValue<T>(val value: T): OptionalValue<T> {
    override fun toString(): String = value.toString()
}

open class OptionalValueSerializer<T>(private val valueSerializer: KSerializer<T>) : KSerializer<OptionalValue<T>> {
    override val descriptor: SerialDescriptor = valueSerializer.descriptor

    override fun deserialize(decoder: Decoder): OptionalValue<T> {
        return try {
            WithValue(valueSerializer.deserialize(decoder))
        } catch (cause: SerializationException) {
            Absent
        }
    }

    override fun serialize(encoder: Encoder, value: OptionalValue<T>) {
        when (value) {
            is Undefined -> {}
            is Absent -> { encoder.encodeNull() }
            is WithValue -> valueSerializer.serialize(encoder, value.value)
        }
    }
}

fun main() {
    val jsonStringPOST = "{\"line1\":\"street\",\"country\":\"GB\"}"
    println("JSON string is: $jsonStringPOST")

    val myAddressPost = Json.decodeFromString<Address2>(jsonStringPOST)
    println("MyAddress object: $myAddressPost")

    val jsonStringUPDATE = "{\"country\":null}"
    println("JSON string is: $jsonStringUPDATE")

    val myAddressUpdate = Json.decodeFromString<Address2>(jsonStringUPDATE)
    println("MyAddress object: $myAddressUpdate")
    if(myAddressUpdate.country is Absent || myAddressUpdate.country is WithValue) {
        println("Update country: ${myAddressUpdate.country}")
    } else {
        println("No update for country: ${myAddressUpdate.country}")
    }
}

输出是:

JSON string is: {"line1":"street","country":"GB"}
MyAddress object: Address2(line1=street, country=GB)
JSON string is: {"country":null}
MyAddress object: Address2(line1=Undefined, country=Absent)
Update country: Absent

关于rest - Kotlin - Ktor - 如何处理 PATCH 调用中的可选 API 资源字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72856270/

相关文章:

java - Jersey REST 扩展方法

rest - 当集合服务返回字典而不是数组时如何使用 Restangular?

android - 在 kotlin 文件中找不到可绘制对象

java - Maybes 是 Scala 的好模式吗?

Kotlin 多变量 let,在下一个 let 中使用前面的变量

java - 如何在没有 web.xml 的情况下实现 jaxrs 应用程序

c# - REST服务切换到https,在浏览器中工作,在c#中抛出 "(403) Forbidden"错误

json - Kotlin 没有将 "is"作为 json key 的开始

android - 当应用程序从 WhatsApp 指纹屏幕等后台恢复时,每次打开启动画面

swift - 为什么我们首先需要可选的绑定(bind)构造