代码之家  ›  专栏  ›  技术社区  ›  mikolaj-jalocha

如何为DTO类添加SerialName注释

  •  0
  • mikolaj-jalocha  · 技术社区  · 7 月前

    我正在使用KTOR作为CMP应用程序的Http客户端。我想知道我是否可以在从API获取数据时省略包装器类,并将其直接加载到DTO中。

    下面是我的设置:

    API响应:

    "data": {
        "id": 11002,
        "name": "name",
        "category": "category"
    }
    

    包装类:

    @Serializable
    data class SearchResponse(
        val data: Dto
    )
    

    最后,DTO的结构与响应完全相同:

    @Serializable
    data class Dto(
        val id: String,
        val name: String,
        val category: String?
    )
    

    每当我在没有包装器类的情况下进行调用时,KTOR都会返回错误。当包含包装器类时,一切正常。是否有方法省略包装器类并直接使用DTO?

    错误:

     io.ktor.serialization.JsonConvertException: Illegal input: Fields [id, name, category] are required for type with serial name 'com.example.dto', but they were missing at path: $
    
    1 回复  |  直到 7 月前
        1
  •  1
  •   Simon Jacobs    7 月前

    您对包装类的API调用之所以有效,是因为响应的形式与您的 SearchResponse 类。 1.

    您可以使用 Dto 如果你写一个 KSerializer 对于 Dto 这将把相同的JSON反序列化为 Dto .

    一种方法是使用 surrogate serializer pattern ,虽然它比平时稍微复杂一些,因为要做到这一点,我们想使用两个序列化器 Dto :Ktor将使用的用户定义的对象,以及用于与代理对象序列化的常规生成的对象(现在移动为 DtoDeserializer.SearchResponse ). 为此,我们使用新 feature 当在上使用自定义序列化程序时,它会保留生成的序列化程序 Dto ,反过来,这种方法是由以下事实序列化器实现的 generic classes 要求序列化器在运行时传递其泛型参数(因此代理对象是泛型的)。

    此代码说明了这样的解决方案。请注意,这仅适用于Kotlin 2.0.20或更高版本以及Kotlin JSON序列化 1.7.2 或稍后。 2.

    /**
     * We keep the generated serializer so that we can use it at the same time as the user-defined serializer
     * [DtoDeserializer]. This requires Kotlin 2.0.20 or later and Kotlin JSON serialization library [1.7.2](https://github.com/Kotlin/kotlinx.serialization/releases/tag/v1.7.2)
     * or later.
     */
    @OptIn(ExperimentalSerializationApi::class)
    @Serializable(DtoDeserializer::class)
    @KeepGeneratedSerializer
    data class Dto(
        val id: String,
        val name: String,
        val category: String?
    )
    
    
    class DtoDeserializer : KSerializer<Dto> {
        private val searchResponseSerializer = SearchResponse.serializer(Dto.generatedSerializer())
    
        override val descriptor: SerialDescriptor
            get() = searchResponseSerializer.descriptor
    
        override fun deserialize(decoder: Decoder): Dto {
            return searchResponseSerializer.deserialize(decoder).data
        }
    
        override fun serialize(encoder: Encoder, value: Dto) {
            error("Not used as Dto is not serialized by app")
        }
    
        /**
         * We make this class generic so that we can supply the serializer for [T] (namely the serializer for
         * [Dto] at runtime
         */
        @Serializable
        private data class SearchResponse<T>(
            val data: T
        )
    }
    

    1. 实际上,只有当API实际返回 String 对于 id 财产。我想这是你写这个问题的错误。

    2. 在早期版本中,您需要另一个看起来完全相同的代理类 Dto 为生成所需的序列化程序 data 财产 反序列化器。搜索响应 .