如何用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
相关产品推荐
相关产品推荐

