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
在我的 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