【问题标题】:Android Retrofit2: Serialize null without GSON or JSONObjectAndroid Retrofit2:在没有 GSON 或 JSONObject 的情况下序列化 null
【发布时间】: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)
    }

【问题讨论】:

    标签: android kotlin retrofit2 json-deserialization android-json


    【解决方案1】:

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

    【讨论】:

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

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

    用例

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

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

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

    【讨论】:

    • 是的,我在这个类中使用用例并捕获错误,但是序列化过程没有任何重要的逻辑......但是如果有疑问,我也会将其添加到描述中.
    猜你喜欢
    • 2021-04-12
    • 2012-02-11
    • 2017-04-01
    • 1970-01-01
    • 2018-02-13
    • 2011-10-14
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    相关资源
    最近更新 更多