I’m tried to save ArrayList<HashMap<String,Uri>> into json using Gson, but when i read data from json , im noticed that Gson just wiped out data inside HashMaps.
I want to that Gson doesn’t wipe my data inside HashMaps anymore.
Here’s my saving code in ConfigurationActivity :
endPlanConfiguration.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), MainActivity.class);
Gson gson = new Gson();
String arraylist = gson.toJson(Singleton.getInstance(v.getContext()).getWorkoutPathList());
SharedPreferences prefs = getSharedPreferences("Workout", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("WorkoutPathList", arraylist);
editor.apply();
startActivity(intent);
finish();
}
});
And here’s code in my MainActivity:
Sry for this tabulation, this is result of many attempts of rewriting code.
public void loadRecView(){
try {
SharedPreferences sharedPreferences = getSharedPreferences("Workout", MODE_PRIVATE);
Gson gson = new Gson();
String json = sharedPreferences.getString("WorkoutPathList", null);
workoutList = gson.fromJson(json, new TypeToken<ArrayList<HashMap<String, Uri>>>() {
}.getType());
recyclerView.setAdapter(new AdapterToDays(MainActivity.this, workoutList));
}
catch (ClassCastException | NullPointerException ex){
Toast.makeText(MainActivity.this, "Определите план тренировок!", Toast.LENGTH_SHORT).show();
}
}
2
Answers
You can instead create a model like this,
Your
HashMap<String, Uri>
can be simplified toList<MyModel>
Use this simplified list as a type in the outer list.
Change
ArrayList<HashMap<String, Uri>>
toList<List<MyModel>>
, this will reduce confusion.Then try to assign the incoming data to this list,
Note: Gson conversion does not modify/delete the data we are passing to it. There might be something wrong with data retrieval in your Singleton class. Check it. Another important thing is that, the data you are passing to Gson should not be an empty string, As you’ve used a try catch block, you are unable to figure out what is the issue. Try to use
Exception
instead ofClassCastException | NullPointerException
, and then log the exception. You might get some clue.Must try with the above code suggestions, if you have not already fixed your issue.
All the best.
The underlying issue is that you are trying to serialize
android.net.Uri
with Gson, but Gson has no built-in adapter for it. You can solve this by either replacingUri
with a type which has built-in Gson support, such asString
, or by writing a custom GsonTypeAdapter
forUri
, see this related question. However, you have to register the adapter withGsonBuilder.registerTypeHierarchyAdapter(...)
since you want it to handle the subclasses ofUri
as well.The following is a more in depth explanation of the issues you encountered, so that troubleshooting future Gson issues becomes easier for you. Some of this are assumptions, which might be slightly incorrect.
With your original code, the reason why the maps where empty and
GsonBuilder.serializeNulls()
made them containnull
values is that:null
but just omits the members from the JSON objectnull
android.net.Uri
is abstract, so it is conceivable that the actual object you are serializing is an instance of an anonymous class.The current source code for
android.net.Uri
does not actually seem to create anonymous classes, but possibly the Android version you are using is doing it, or maybe there is something different involved which makes Gson think the class is local or anonymous.Regarding the second issue you encountered after you changed your code:
As mentioned above, Gson has no built-in adapter for
android.net.Uri
.The reason why you were not seeing this exception with your previous code is that as you mentioned the maps in JSON were empty, so Gson never tried to construct a
android.net.Uri
.But with your changed code Gson was serializing the
Uri
as non-null
values (see below for a possible explanation), and therefore also trying to deserialize them again now.Regarding the third issue, after you switched from
Uri
toString
:At this point you had probably managed to serialize the
Uri
values to JSON, probably because when the field had typeUri
Gson not only considered the runtime class (getClass()
), which was serialized asnull
previously, but also the compile-time classUri
of the field[1].But because you still hadn’t registered a custom adapter, Gson was using the reflection-based adapter and serialized the
Uri
as JSON object ({ ... }
)[2]. So when you then changedUri
toString
you probably still had the old JSON data in the shared preferences so Gson was trying to serialize the previous JSON object{ ... }
of theUri
as JSON string" ... "
.To solve this, just remove the old entry from the shared preferences.
[1]: See Gson’s internal
TypeAdapterRuntimeTypeWrapper
for more details.[2]: Relying on the reflection-based adapter for third-party classes should be avoided because you then depend on their implementation details which can change at any point.