当前位置: 首页 > 知识库问答 >
问题:

@formurlencoded@field枚举未使用自定义Moshi适配器

明阳旭
2023-03-14

我在我的应用程序中使用了reverfit(V2.9.0)和Moshi(V1.11.0)。我尝试这样调用endpoint:

@FormUrlEncoded
@PATCH("anime/{anime_id}/my_list_status")
fun updateListStatus(
    @Path("anime_id") animeId: Long,
    @Field("num_watched_episodes") nbWatchedEpisodes: Int,
    @Field("score") score: Double,
    @Field("status") watchStatus: WatchStatus,
): Single<MyListStatus>

但是WatchStatus->JSON转换没有像预期的那样工作。WatchStatus是一个简单的枚举类:

enum class WatchStatus {
    COMPLETED,
    DROPPED,
    ON_HOLD,
    PLAN_TO_WATCH,
    WATCHING,
}

我创建了一个自定义适配器,因为我的应用程序使用大写枚举名称,而后端使用小写名称:

class AnimeMoshiAdapters {

    /* Others adapters */

    @ToJson
    fun watchStatusToJson(watchStatus: WatchStatus): String = 
watchStatus.toString().toLowerCase(Locale.getDefault())

    @FromJson
    fun watchStatusFromJson(watchStatus: String): WatchStatus =
        WatchStatus.valueOf(watchStatus.toUpperCase(Locale.getDefault()))
}

我以以下方式创建Moshi实例:

Moshi.Builder()
        .addLast(KotlinJsonAdapterFactory())
        .add(AnimeMoshiAdapters())
        .build()

并且我的改型实例使用它(带有Koin注入):

Retrofit.Builder()
        .baseUrl(get<String>(named("baseUrl")))
        .client(get(named("default")))
        .addConverterFactory(MoshiConverterFactory.create(get()))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()

解析Json以创建WatchStatus枚举时,将使用适配器。这是很明显的,因为如果我移除自定义适配器,调用失败并出现错误“com.squareup.moshi.jsonDataException:Expected one of[COMPLETED,drop,ON_HOLD,PLAN_TO_WATCH,wating]”。当我试图调用上面指定的endpoint时,Json中WatchStatus的转换是错误的,枚举名保持大写,这意味着没有使用我的自定义适配器。如果我检查修改日志,我可以看到它发送“num_watched_episodes=12&score=6.0&status=on_hold”,所以状态不是用小写转换的。

如果我尝试使用相同的Moshi实例手动转换Json中的WatchStatus,它会按照预期工作,因此我相信我的自定义适配器实现是正确的。

我如何在这个调用中使用我的自定义Moshi适配器?

共有1个答案

昌砚
2023-03-14

Moshi适配器的tojson应用于改装请求的主体(==带有@body注释的params),而不是查询参数(==带有@field注释的params)。这是正确的和预期的行为,因为按照标准,查询参数不期望是JSON格式的字符串(即使在您的例子中,它只是一个字符串)。如果您的服务器希望status字段作为查询参数,那么除了自己提供小写字段之外,没有其他方法:

@FormUrlEncoded
@PATCH("anime/{anime_id}/my_list_status")
fun updateListStatus(
    ...
    @Field("status") watchStatus: String
): Single<MyListStatus>

并向updateListStatus提供已经小写的值:

updateListStatus(..., COMPLETED.name.toLowerCase())

如果您对服务器的实现没有影响,那么就跳过这篇文章的其余部分。

如果希望利用自定义适配器的toJSON函数,服务器需要更改请求以接受JSON正文而不是查询参数,如下所示:

PUT: anime/{anime_id}/my_list_status
BODY:
{
  "anime_id" : Long,
  "num_watched_episodes" : Int,
  "score" : Double,
  "status" : WatchStatus
}

然后您将为主体创建一个数据类,例如命名为requestbody,然后您可以将请求更改为:

@PUT("anime/{anime_id}/my_list_status")
fun updateListStatus(
    ...
    @BODY body: RequestBody
): Single<MyListStatus>

在这种情况下,您的自定义适配器将生效并通过其定义的ToJSON逻辑转换RequestBody内部的WatchStatus

 类似资料:
  • 在为Android项目配置Kotlin之后,我编写了一个简单的。它调用了reverfit来获得一个包含以下数据的JSON文件: 现在我想使用Moshi将JSON数据转换为Kotlin的类,所以下面是反映上述JSON结构的两个类: 和Moshi的自定义类型适配器: 当它进入函数时,就像预期的那样。但奇怪的是,是,应该是。 我对莫希和科特林是新的,我已经在这个问题上坚持了大约10个小时。请帮帮我。谢谢

  • 我使用一个API,它以数组的形式返回数据,比如这个JSON字符串(我们称之为“任务”) 我想将这个响应解析为一个列表。所以我已经用生成的适配器创建了相应的数据类 我的改装服务是这样的 但我想使用容器类型,例如: 作为网络响应结果。这可能不创建自定义adpter或者我将需要创建一个自定义适配器并将其传递给moshi构建器吗?

  • 让我们看看一个需要诉诸于代码的场景,来考虑为何此时使用枚举更为合适且实用。假设我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准:IPv4(version four)和 IPv6(version six)。这是我们的程序可能会遇到的所有可能的 IP 地址类型:所以可以 枚举 出所有可能的值,这也正是此枚举名字的由来。 任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的,而且不能

  • enum 关键字允许创建一个代表数个可能变量的数据的类型(原文:The enum keyword allows the creation of a type which may be one of a few different variants.若您对此句有 更好的翻译或理解,希望指出来,谢谢。)。在 struct 中任何合法的变量在 enum 同样是合法的。 // 隐藏未使用代码警告的属性。

  • 问题内容: 我想要一个Java枚举,其值是整数。 例如: 但是我也想要这两个常量的自定义名称, 例如“ Task Created”和“ Task Deleted”(那里有空格)。 我想尽可能优雅地做到这一点,而无需编写 太多额外的代码。 我可以在没有其他 将枚举常量映射到其自定义名称的映射的情况下实现此目标吗? 我在该项目中使用JDK 6。 问题答案: 只需为此添加一个字段: 如果您不想指定字符串

  • 在不使用其他映射( 将枚举常量映射到它们的自定义名称)的情况下,我能做到这一点吗? 我在这个项目中使用JDK6。