Retrofit 从 Map<String : Any> 生成错误的 GET 请求参数

Retrofit generates wrong GET request parameters from Map<String : Any>

我有以下数据:

var dict: Map<String, Any> = listOf()
dict["p1"] = listOf(1, 3)
dict["p2"] = listOf(null, 2.1)
dict["p3"] = 1

当我将此数据传递给以下函数时:

@GET("uri?staticKey=staticValue")
fun testApi(@QueryMap(encoded = true) params: @JvmSuppressWildcards Map<String, Any>): Call<ResponseBody>

我希望请求 URL 是:

uri?staticKey=staticValue&
    p1[0]=1&p1[1]=3&
    p2[0]=&p2[1]=2.1&
    p3=1

但这是它产生的结果:

uri?staticKey=staticValue&
    p1=[1, 3]&
    p2=[null, 2.1]&
    p3=1

我是不是做错了什么?我刚刚开始使用 Kotlin 和 Android 开发,所以我不确定 Retrofit/okhttp 库是否支持它。

注意我需要Map<String, Any>这样的功能,以便轻松add/delete查询参数。

Retrofit 只是在 Map<String, Any> 的值上调用 toString()。要实现你想要的,你必须直接定义它

var dict: Map<String, Any> = listOf()
dict["p1[0]"] = 1
dict["p1[1]"] = 3
dict["p2[0]"] = ""
dict["p2[1]"] = 2.1
dict["p3"] = 1

您只需将地图展平即可绘制地图

fun Map<String, Any>.queryArgs(): Map<String, Any> {
    val map = mutableMapOf<String, Any>()
    forEach { (key, value) ->
        if (value !is Iterable<*>) {
            map[key] = value
        } else value.forEachIndexed { index, value ->
            map["$key[$index]"] = value ?: ""
        }
    }
    return map
}

这假设最多只有一层列表。

这是我提供的解决方案


  • HashMap中的原始key,这样updating/deleting是可以的,比如

    dict.remove("p1")
    dict["p1"] = new value
    
  • 能够嵌套数组,例如

    [
      0: [coord1, coord2, coord3],
      1: [coord1, coord2, coord3]
    ]
    

data class URL(val params: MutableMap<String, Any> = mutableMapOf<String, Any>()) {

    override fun toString(): String = params.map {
        val value = it.value
        if (value !is Iterable<*>) {
            it.key + "=" + it.value.toString()
        } else {
            createPairs(it.key, value)
        }
    }.joinToString("&")

    fun createPairs(key: String, value: Iterable<*>): String {
        return value.mapIndexed { idx, value ->
            val useKey = key + "[" + idx + "]"
            if (value !is Iterable<*>) {
                useKey + "=" + (value?.toString() ?: "")
            } else
                createPairs(useKey, value)

        }.joinToString("&")
    }
}

用法:

val url = URL()
url.params["p1"] = listOf(1, 3)
url.params["p2"] = listOf(null, 2.1)
url.params["p3"] = 1

val polygon1 = listOf("p1-coord1", "p1-coord2")
val polygon2 = listOf("p2-coord1", "p2-coord2", "p2-coord3")
val polygon3 = listOf("p3-coord1", "p3-coord2")
url.params["bbox"] = listOf(polygon1, polygon2, polygon3)

println(url.toString())

输出:

p1[0]=1&p1[1]=3&
p2[0]=&p2[1]=2.1&
p3=1&
bbox[0][0]=p1-coord1&bbox[0][1]=p1-coord2&
bbox[1][0]=p2-coord1&bbox[1][1]=p2-coord2&bbox[1][2]=p2-coord3&
bbox[2][0]=p3-coord1&bbox[2][1]=p3-coord2