skip to Main Content

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


  1. You can instead create a model like this,

    public class MyModel {
    
        private String name;
        private Uri uri;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Uri getUri() {
            return uri;
        }
    
        public void setUri(Uri uri) {
            this.uri = uri;
        }
    }
    

    Your HashMap<String, Uri> can be simplified to List<MyModel>
    Use this simplified list as a type in the outer list.

    Change ArrayList<HashMap<String, Uri>> to List<List<MyModel>>, this will reduce confusion.

    Then try to assign the incoming data to this list,

    SharedPreferences sharedPreferences = getSharedPreferences("Workout", MODE_PRIVATE);
    String json = sharedPreferences.getString("WorkoutPathList", null);
    List<List<MyModel>> workOutList = new Gson().fromJson(json, new TypeToken<List<List<MyModel>>>() {}.getType());
    

    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 of ClassCastException | 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.

    Login or Signup to reply.
  2. 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 replacing Uri with a type which has built-in Gson support, such as String, or by writing a custom Gson TypeAdapter for Uri, see this related question. However, you have to register the adapter with GsonBuilder.registerTypeHierarchyAdapter(...) since you want it to handle the subclasses of Uri 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 contain null values is that:

    • By default Gson does not serialize null but just omits the members from the JSON object
    • If Gson encounters local or anonymous classes it serializes them as null
      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:

    Abstract classes can’t be instantiated! Register an InstanceCreator or a TypeAdapter for this type. Class name: android.net.Uri

    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 to String:

    IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 45 path $[0][0].uri

    At this point you had probably managed to serialize the Uri values to JSON, probably because when the field had type Uri Gson not only considered the runtime class (getClass()), which was serialized as null previously, but also the compile-time class Uri 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 changed Uri to String you probably still had the old JSON data in the shared preferences so Gson was trying to serialize the previous JSON object { ... } of the Uri 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search