Android Retrofit2:在没有 GSON 或 JSONObject 的情况下序列化 null

Posted

技术标签:

【中文标题】Android Retrofit2:在没有 GSON 或 JSONObject 的情况下序列化 null【英文标题】:Android Retrofit2: Serialize null without GSON or JSONObject 【发布时间】:2021-12-27 16:09:19 【问题描述】:

我的 android 应用程序中 API 调用的结果可以是一个带有配置的 JSON,它映射到 SupportConfigurationJson 类,或者只是纯 null。当我得到一个 JSON 时,应用程序可以正常工作,但是当我得到 null 时,我得到了这个异常:

kotlinx.serialization.json.internal.JsonDecodingException: Expected start of the object '', but had 'EOF' instead
    JSON input: null

我应该避免在这个项目中使用GSON。我还找到了一个解决方案,API 接口将返回Response<JSONObject>,之后我的存储库应检查此JSONObject 是否为空,如果不是,则将其映射到SupportConfigurationJson。但是在项目中,我们总是使用自定义类的响应,所以我想知道,有没有其他解决方案可以使用 null 或自定义数据类获得响应?

GettSupportConfiguration 用例类:

class GetSupportConfiguration @Inject constructor(
    private val supportConfigurationRepository: SupportConfigurationRepository
) 
    suspend operator fun invoke(): Result<SupportConfiguration?> 
        return try 
            success(supportConfigurationRepository.getSupportConfiguration())
         catch (e: Exception) 
            /*
            THIS SOLUTION WORKED, BUT I DON'T THINK IT IS THE BEST WAY TO SOLVE THE PROBLEM
            if (e.message?.contains("JSON input: null") == true) 
                success(null)
             else 
                failure(e)
            
            */ 
                //I WAS USING THROW HERE TO SEE WHY THE APP ISN'T WORKING PROPERLY
            //throw(e)
            failure(e)
        
    

SupportConfigurationJson 类:


@Serializable
data class SupportConfigurationJson(
    @SerialName("image_url")
    val imageUrl: String,
    @SerialName("description")
    val description: String,
    @SerialName("phone_number")
    val phoneNumber: String?,
    @SerialName("email")
    val email: String?
)

SupportConfigurationRepository 类:

@Singleton
class SupportConfigurationRepository @Inject constructor(
    private val api: SupportConfigurationApi,
    private val jsonMapper: SupportConfigurationJsonMapper
) 

    suspend fun getSupportConfiguration(): SupportConfiguration? =
        mapJsonToSupportConfiguration(api.getSupportConfiguration().extractOrThrow())


    private suspend fun mapJsonToSupportConfiguration(
        supportConfiguration: SupportConfigurationJson?
    ) = withContext(Dispatchers.Default) 
        jsonMapper.mapToSupportSettings(supportConfiguration)
    

fun <T> Response<T?>.extractOrThrow(): T? 
    val body = body()
    return if (isSuccessful) body else throw error()


fun <T> Response<T>.error(): Throwable 
    val statusCode = HttpStatusCode.from(code())
    val errorBody = errorBody()?.string()
    val cause = RuntimeException(errorBody ?: "Unknown error.")

    return when 
        statusCode.isClientError -> ClientError(statusCode, errorBody, cause)
        statusCode.isServerError -> ServerError(statusCode, errorBody, cause)
        else -> ResponseError(statusCode, errorBody, cause)
    

SupportConfigurationApi 类:

interface SupportConfigurationApi 

    @GET("/mobile_api/v1/support/configuration")
    suspend fun getSupportConfiguration(): Response<SupportConfigurationJson?>

SupportConfigurationJsonMapper 类:

class SupportConfigurationJsonMapper @Inject constructor() 

    fun mapToSupportSettings(json: SupportConfigurationJson?): SupportConfiguration? 
        return if (json != null) 
            SupportConfiguration(
                email = json.email,
                phoneNumber = json.phoneNumber,
                description = json.description,
                imageUrl = Uri.parse(json.imageUrl)
            )
         else null
    

我这样创建改造:

@Provides
    @AuthorizedRetrofit
    fun provideAuthorizedRetrofit(
        @AuthorizedClient client: OkHttpClient,
        @BaseUrl baseUrl: String,
        converterFactory: Converter.Factory
    ): Retrofit 
        return Retrofit.Builder()
            .client(client)
            .baseUrl(baseUrl)
            .addConverterFactory(converterFactory)
            .build()
    
@Provides
    @ExperimentalSerializationApi
    fun provideConverterFactory(json: Json): Converter.Factory 
        val mediaType = "application/json".toMediaType()
        return json.asConverterFactory(mediaType)
    

【问题讨论】:

【参考方案1】:

一切都解释了here(1分钟阅读) Api 应该为 null 返回 "",如果您无法更改 API,请将此转换器添加到 Retrofit

【讨论】:

那行得通。非常感谢您分享此资源!【参考方案2】:

您正在与您的存储库直接交互,我建议使用

用例

与数据层交互。 因为您没有在此处捕获此异常,所以您的应用正在崩溃

suspend fun getSupportConfiguration(): SupportConfiguration? =
        mapJsonToSupportConfiguration(api.getSupportConfiguration().extractOrThrow())

Usecase 通常会捕获这些错误并在 ui 中显示有用的错误消息。

【讨论】:

是的,我在这个类中使用用例并捕获错误,但是序列化过程没有任何重要的逻辑......但是如果有疑问,我也会将其添加到描述中.

以上是关于Android Retrofit2:在没有 GSON 或 JSONObject 的情况下序列化 null的主要内容,如果未能解决你的问题,请参考以下文章

Android RxJava2+Retrofit2单文件下载监听进度封装

深度详解Retrofit2使用实践

深度详解Retrofit2使用实践

Android 使用Retrofit2.0+OkHttp3.0实现缓存处理+Cookie持久化第三方库

我的Android进阶之旅如何在Retrofit2 中创建动态 URL?

retrofit2.HttpException:Android 中的 HTTP 302