You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit 结合 Moshi:将 JsonArray 转换为字符串

解决Moshi将JSON数组字段解析为原始字符串的问题

这个场景我之前处理过,核心思路是给Moshi编写一个自定义Json适配器,让它把原本应该解析成数组的字段直接转成原始JSON字符串。下面结合你的代码结构一步步实现:

1. 编写自定义Json适配器

这个适配器负责把JsonReader读取到的JSON数组(或任意JSON元素)转换成字符串,同时支持把字符串写回成JSON格式(如果需要上传的话):

class RawJsonStringAdapter {
    // 从JSON解析为字符串:读取整个JSON元素并转成原始字符串
    @FromJson
    fun fromJson(reader: JsonReader): String {
        // readJsonValue()会读取当前位置的整个JSON元素(这里是数组),返回对应的Kotlin对象,再转成字符串
        return reader.readJsonValue().toString()
    }

    // 把字符串写回JSON:将原始字符串解析为JSON值再写入
    @ToJson
    fun toJson(writer: JsonWriter, rawJsonString: String?) {
        if (rawJsonString == null) {
            writer.nullValue()
            return
        }
        // 临时用Moshi把字符串解析回JSON对象,再写入到输出流
        val jsonValue = Moshi.Builder().build().adapter(Any::class.java).fromJson(rawJsonString)
        writer.value(jsonValue)
    }
}

2. 修改Book实体类

给params字段指定我们的自定义适配器,有两种方式:

方式一:直接用@JsonAdapter注解

直接在字段上绑定适配器:

data class Book(
    val id: String,
    val name: String,
    // 指定用自定义适配器解析这个字段
    @JsonAdapter(RawJsonStringAdapter::class)
    val params: String
)

方式二:用自定义注解(更通用)

如果多个字段都需要这种解析方式,可以定义一个注解,把适配器和注解关联:

// 定义一个限定符注解
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class RawJson

// 修改适配器,关联这个注解
class RawJsonStringAdapter {
    @FromJson
    @RawJson
    fun fromJson(reader: JsonReader): String {
        return reader.readJsonValue().toString()
    }

    @ToJson
    fun toJson(writer: JsonWriter, @RawJson rawJsonString: String?) {
        if (rawJsonString == null) {
            writer.nullValue()
            return
        }
        val jsonValue = Moshi.Builder().build().adapter(Any::class.java).fromJson(rawJsonString)
        writer.value(jsonValue)
    }
}

然后Book类里用这个注解标记字段:

data class Book(
    val id: String,
    val name: String,
    @RawJson
    val params: String
)

3. 配置Retrofit和Moshi实例

创建Moshi的时候一定要把自定义适配器添加进去,否则注解不会生效:

// 初始化带自定义适配器的Moshi
val moshi = Moshi.Builder()
    .add(RawJsonStringAdapter()) // 关键:添加我们的适配器
    .build()

// 初始化Retrofit,使用配置好的Moshi
val retrofit = Retrofit.Builder()
    .baseUrl("你的服务器基础地址")
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()

4. 保持Retrofit接口不变

你的请求接口不需要修改,还是正常定义返回List<Book>:

interface BookApi {
    @GET("books")
    suspend fun getBooks(): List<Book>
}

原理说明

当Moshi解析params字段时,会触发我们的fromJson方法:reader.readJsonValue()会一次性读取整个JSON数组(不管数组里的元素结构),然后转成原始的JSON字符串,这样params字段就会被赋值为服务器返回的数组原始文本了。

如果后续需要把params作为JSON数组上传回服务器,toJson方法会把字符串解析回JSON对象再写入,确保服务器能正确识别格式。

内容的提问来源于stack exchange,提问作者deadfish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:58:52