After updating the targetSdk to 31, gson.ToJson started giving empty results for List<File>
on android 12 device (vivo v2036). Tried passing TypeToken as well still remains the same. Funny thing is that its working fine on lower androids and on targetSdk 30.
public void save(Context context, List<File> files) {
Gson gson = new Gson();
String json = gson.toJson(files);
//getting json value as "[{}]"
}
Gson has no built-in adapter for java.io.File
so it falls back to using reflection. This should be avoided for third-party classes because it accesses their internal fields which makes you dependent on their internal implementation. That internal implementation could change at any point because it is not part of the public API, and can therefore break your JSON serialization logic.
As mentioned by @CommonsWare you can either change the type of the list to List<String>
and by doing so only use types for which Gson has built-in adapters. Or you can also solve this by registering a custom TypeAdapter
for File
:
class FileTypeAdapter extends TypeAdapter<File> {
@Override
public void write(JsonWriter out, File file) throws IOException {
if (file == null) {
out.nullValue();
} else {
out.value(file.getPath());
}
}
@Override
public File read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
} else {
return new File(in.nextString());
}
}
}
(Coincidentally this custom TypeAdapter
also produces more compact JSON because it serializes the File
as a JSON string value whereas the reflection-based adapter would serialize it as JSON object with JSON string property.)
You can then register the adapter like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(File.class, new FileTypeAdapter())
.create();
If that does not solve your issue, you could also try registering the adapter with GsonBuilder.registerTypeHierarchyAdapter
. Possibly Android is creating instances of custom File
subclasses.
To detect any cases where you accidentally depend on reflection-based serialization for Android classes you can use GsonBuilder.addReflectionAccessFilter(ReflectionAccessFilter.BLOCK_ALL_ANDROID)
(requires Gson 2.9.1 or newer).