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

如何用Moshi替换Gson TypeToken?JSON解析编码异常求解

用Moshi替换Gson的TypeToken实现JSON解析并解决编码异常

API响应示例

{"api":"ApiName","status":"500","errorMessages":[{"errorCd":"errorCode","errorDtl":"Not found XXXXXXXXXX"}]}

当前Gson实现代码

@JsonClass(generateAdapter = true)
data class DataClass<T>(
    @Json(name = "api")
    val api: String,
    @Json(name = "data")
    val data: T?,
    @Json(name = "x")
    val x: X?,
    @Json(name = "status")
    val status: String,
    @Json(name = "y")
    val y: String?,
    @Json(name = "errorMessages")
    val error: List<Error>?,
)


val gson = Gson()
val type = object : TypeToken<DataClass<Unit>>() {}


response.errorBody()?.charStream()?.let {
    try {
        val errorResponse: DataClass<Unit>? = gson.fromJson(it, type)
        Log.d("SUNIL", "safeApiCallInSide: $errorResponse")
        errorResponse?.error?.first()?.let { error ->
            ResultEither.Left(
                Failure.ServerError(
                    code = error.errorCd, errorKey = error.errorDtl.toString()
                )
            )
        } ?: ResultEither.Left(Failure.ServerError(code = "generic_error"))
    } catch (e: Throwable) {
        ResultEither.Left(
            Failure.ServerError(
                code = "generic_error", errorKey = "Internal Server Error"
            )
        )
    }
}

尝试的Moshi实现代码(未达预期)

val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val t = Types.newParameterizedType(
    DataClass::class.java, Types.newParameterizedType(
        List::class.java,
        Error::class.java
    )
)

response.errorBody()?.charStream()?.let {
    try {
        val adapter = moshi.adapter<DataClass<List<Error>>>(t)
        val error: ApiResponse<List<Error>>? = adapter.fromJson(it.toString())
        error?.error?.first()?.let { error ->
            ResultEither.Left(
                Failure.ServerError(
                    code = error.errorCd, key= error.errorDtl.toString()
                )
            )
        } ?: ResultEither.Left(Failure.ServerError(code = "generic_error"))
    } catch (e: Throwable) {
        ResultEither.Left(
            Failure.ServerError(
                code = "generic_error", key= "Internal Server Error"
            )
        )
    }
}

报错信息

BaseNetworkRepository:com.squareup.moshi.JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $


正确的Moshi替换方案及异常解决

1. 修正泛型类型匹配(对应Gson的TypeToken)

原Gson代码解析的是DataClass<Unit>(错误响应无data字段,用Unit作为泛型参数更合理)。Moshi中用Types.newParameterizedType构造带泛型的Type,完全对应Gson的TypeToken功能:

// 构造DataClass<Unit>的Type,替代Gson的TypeToken<DataClass<Unit>>
val dataClassType = Types.newParameterizedType(DataClass::class.java, Unit::class.java)

2. 解决JsonEncodingException异常

报错核心原因是调用了it.toString(),把Reader转换成了对象的字符串标识(如java.io.InputStreamReader@xxxxxx),而非实际JSON内容。直接传入Reader给fromJson即可。若存在JSON格式不规范问题,可额外开启lenient宽松模式。

完整修正后的代码

// 初始化Moshi,必须添加KotlinJsonAdapterFactory支持Kotlin数据类
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

// 构造目标泛型类型
val dataClassType = Types.newParameterizedType(DataClass::class.java, Unit::class.java)

response.errorBody()?.charStream()?.let { reader ->
    try {
        // 获取对应类型的Adapter
        val adapter = moshi.adapter<DataClass<Unit>>(dataClassType)
        // 直接传入Reader,禁止调用toString()
        val errorResponse: DataClass<Unit>? = adapter.fromJson(reader)
        
        Log.d("SUNIL", "safeApiCallInSide: $errorResponse")
        
        errorResponse?.error?.first()?.let { error ->
            ResultEither.Left(
                Failure.ServerError(
                    code = error.errorCd, errorKey = error.errorDtl.toString()
                )
            )
        } ?: ResultEither.Left(Failure.ServerError(code = "generic_error"))
    } catch (e: JsonEncodingException) {
        // 若JSON格式不规范,启用宽松模式重试
        val lenientAdapter = moshi.adapter<DataClass<Unit>>(dataClassType)
            .lenient()
        val errorResponse: DataClass<Unit>? = lenientAdapter.fromJson(reader)
        
        errorResponse?.error?.first()?.let { error ->
            ResultEither.Left(
                Failure.ServerError(
                    code = error.errorCd, errorKey = error.errorDtl.toString()
                )
            )
        } ?: ResultEither.Left(Failure.ServerError(code = "generic_error"))
    } catch (e: Throwable) {
        ResultEither.Left(
            Failure.ServerError(
                code = "generic_error", errorKey = "Internal Server Error"
            )
        )
    }
}

额外注意事项

  • 确保Error数据类也添加@JsonClass(generateAdapter = true)注解,否则Moshi无法自动生成适配器:
@JsonClass(generateAdapter = true)
data class Error(
    @Json(name = "errorCd")
    val errorCd: String,
    @Json(name = "errorDtl")
    val errorDtl: String
)
  • 原代码中的ApiResponse是笔误,需改为DataClass,修正代码已同步调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:25:35