Вопрос или проблема
var retrofitResponse: RetrofitResponse<APIResponse<User>>? = null
withContext(Dispatchers.IO) {
retrofitResponse = try {
val response = BindRetrofitWithService.getServiceInstance(
needCustomTimeOut = Config.isChinaBuild(appContext)
).registerUser(body, data)
RetrofitResponse(true, response)
} catch (e: Exception) {
RetrofitResponse(false, e.getAPIError())
}
}
return retrofitResponse!!
fun getServiceInstance(needCustomTimeOut: Boolean = false): APIServices {
if (GeneralUtils.isNetworkAvailable()) {
return getRetrofit(needCustomTimeOut).create(APIServices::class.java)
} else {
throw NoNetworkException()
}
}
fun getRetrofit(needCustomTimeOut: Boolean = false): Retrofit {
return getRetrofitObject(
null, if (needCustomTimeOut) {
CHINA_FILE_UPLOAD_CONNECT_TIME_OUT
} else {
CONNECT_TIME_OUT
}else {
WRITE_TIME_OUT
}
)
}
/**
* Создание пользовательского экземпляра Retrofit
*/
private fun getRetrofitObject(
headers: HashMap<String, String>?,
connectionTimeOut: Long,
readTimeOut: Long,
writeTimeOut: Long
): Retrofit {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient.Builder()
//okHttpClient.addInterceptor(MockInterceptor())
okHttpClient.addInterceptor { chain ->
//if(GeneralUtils.isNetworkAvailable()){
val оригинал = chain.request()
val requestBuilder = оригинал.newBuilder()
if (headers != null && headers.size > 0) {
for ((key, value) in headers) {
requestBuilder.addHeader(key, value)
}
}
val request = requestBuilder.build()
return@addInterceptor chain.proceed(request)
/*}else{
throw NoNetworkException()
}*/
}
okHttpClient.addInterceptor(loggingInterceptor)
okHttpClient.connectTimeout(connectionTimeOut, TimeUnit.SECONDS)
okHttpClient.readTimeout(readTimeOut, TimeUnit.SECONDS)
okHttpClient.writeTimeout(writeTimeOut, TimeUnit.SECONDS)
return Retrofit.Builder()
.baseUrl(Config.getRetrofitBaseUrl(UCHContext.getAppContext()))
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.client(okHttpClient.build())
.build()
}
класс модели пользователя
@SerializedName(DBConstants.COL_USER_PROFILE_NOTE)
@ColumnInfo(name = DBConstants.COL_USER_PROFILE_NOTE)
@Nullable
public String note = null;
@SerializedName(DBConstants.COL_USER_PROFILE_TITLE)
@ColumnInfo(name = DBConstants.COL_USER_PROFILE_TITLE_VAL)
@Nullable
public String titled = null;
@SerializedName(DBConstants.COL_USER_PROFILE_ADDRESSES)
@ColumnInfo(name = DBConstants.COL_USER_PROFILE_ADDRESSES)
@Nullable
public JsonObject addresses= new JsonObject();
Примечание:
иногда “addresses”:null приводит к следующей ошибке
#com.google.gson.JsonSyntaxException: Ожидался com.google.gson.JsonObject, но был com.google.gson.JsonNull; на пути
$.data.addresses
#responseCode= 1208, responseCodeMessage= Сбой API, userMessage= Что-то пошло не так, пожалуйста, попробуйте снова.
как решить эту проблему? Я пробовал пользовательский и стандартный классы Gson, но это не помогло.
Ответ или решение
Ошибка com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonNull
возникает, когда ваш код ожидает, что поле addresses
должно быть объектом JsonObject
, но фактически оно приходит как null
. Чтобы устранить эту проблему, вы можете применить несколько подходов.
1. Изменение модели данных
Вместо того чтобы использовать JsonObject
для поля addresses
, вы можете использовать Object
и обрабатывать его как null
. Это делается для того, чтобы Gson мог корректно десериализовать null
:
@SerializedName(DBConstants.COL_USER_PROFILE_ADDRESSES)
@ColumnInfo(name = DBConstants.COL_USER_PROFILE_ADDRESSES)
@Nullable
public Object addresses = null; // Измените JsonObject на Object
2. Создание кастомного десериализатора
Если вам необходимо продолжать использовать JsonObject
(например, когда вам нужно проводить операции с объектом), вы можете написать кастомный десериализатор:
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
User user = new User();
user.note = jsonObject.get(DBConstants.COL_USER_PROFILE_NOTE).getAsString();
user.titled = jsonObject.get(DBConstants.COL_USER_PROFILE_TITLE).getAsString();
// Проверяем, есть ли поле addresses и не является ли оно null
JsonElement addressesElement = jsonObject.get(DBConstants.COL_USER_PROFILE_ADDRESSES);
if (addressesElement != null && !addressesElement.isJsonNull()) {
user.addresses = addressesElement.getAsJsonObject();
} else {
user.addresses = new JsonObject(); // Или null, в зависимости от того, что вам нужно
}
return user;
}
}
Затем зарегистрируйте десериализатор в вашей конфигурации Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserDeserializer())
.create();
3. Обработка ответа в коде
При выполнении вызова API необходимо также убедиться, что вы правильно обрабатываете null
значения после получения ответа:
val response = BindRetrofitWithService.getServiceInstance(...)
response?.let {
if (it.data.addresses == null) {
// Обработка случая, когда addresses равно null
// Например, инициализация пустого JsonObject
it.data.addresses = JsonObject()
}
}
4. Изменение API, если возможно
В идеале, если возможно, корректируйте серверную часть, чтобы она возвращала объект JSON вместо null
. Например, вместо того чтобы возвращать:
{
"addresses": null
}
Сервер должен возвращать:
{
"addresses": {}
}
Заключение
Каждое из предложенных решений подходит для разных сценариев. Вы можете выбрать самое подходящее для вашей ситуации. Если есть возможность изменить API — это самый надежный способ. Если API не поддается изменению, используйте кастомные десериализаторы или корректируйте обработку на клиенте. Таким образом, ваша программа будет готова к правильной обработке null
полей без сбоев.