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

Android Retrofit2: Serialize null without GSON or JSONObject

在我的 Android 应用程序中调用 API 的结果可以是 JSON,其配置映射到 SupportConfigurationJson class,或者只是纯粹的null。当我得到 JSON 时,应用程序正常运行,但是当我得到 null 时,我得到这个异常:

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

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

GettSupportConfiguration 用例 class:

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 class:


@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 class:

@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 class:

interface SupportConfigurationApi {

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

SupportConfigurationJsonMapperclass:

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
    }
}

我这样创建 Retrofit:

@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)
    }

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

usecases

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

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

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

一切都解释清楚了here(1 分钟阅读) Api 应该 return "{}" for null, 如果你不能改变 API 将此转换器添加到 Retrofit