Problem Description
I encountered a problem when using Retrofit to initiate network requests. The console became popular and the following error message was continuously output:
Parcel: Reading a NULL string not supported here.
Cause Analysis
In the request callback processing Retrofit, use System.out.println(t.getMessage())
to print exception information.
@Override
public void onFailure(Call<Result<List<Animal>>> call, Throwable t) {
System.out.println(t.getMessage());
}
By printing exception information, the following errors are found:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 230 path $.data[0].updateTime
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 195 path $.data[0].createTime
According to the error message analysis, the problem occurs when using Retrofit combined with GsonConverterFactory to parse JSON data containing LocalDateTime type createTime
and updateTime
. This error usually means that the JSON data does not conform to the data structure expected by the code. The code expected an object (BEGIN_OBJECT) but actually encountered a string (STRING).
Since LocalDateTime is not a type supported by Gson by default, a custom adapter needs to be used to solve this problem.
solution
- Create a custom Gson adapter to handle the LocalDateTime type. This can be achieved by extending Gson's TypeAdapter. Create a LocalDateTimeAdapter class.
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
@Override
public void write(JsonWriter out, LocalDateTime value) throws IOException {
if (value != null) {
out.value(FORMATTER.format(value));
} else {
out.nullValue();
}
}
@Override
public LocalDateTime read(JsonReader in) throws IOException {
if (in.hasNext()) {
String value = in.nextString();
return LocalDateTime.parse(value, FORMATTER);
} else {
return null;
}
}
}
- When creating Retrofit, use GsonBuilder to register the custom adapter into Gson. You can create a RetrofitBuilder class that is used to create Retrofit instances.
import com.example.pawprint.adapter.LocalDateTimeAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.LocalDateTime;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitBuilder {
public static Retrofit build(String baseUrl) {
// 创建 Gson 实例
Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
.create();
// 创建 Retrofit 实例
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
}
- Just call the build static method of RetrofitBuilder directly where you need to create a Retrofit instance.
String baseUrl = getString(R.string.base_url);
retrofit = RetrofitBuilder.build(baseUrl);
After the above processing, the problem is solved, Retrofit no longer reports an error, and the data of createTime and updateTime can be obtained normally.