Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing a SparseArray<T> with GSON

I want to serialize a custom Java object, so I can use SharedPreferences to store it and retreive it in another Activity. I don't need persistant storage, the SharedPreferences, I wipe them when my application is closed. I'm currently using GSON for this, but it doesn't seem to work well with Android's SparseArray type.

My objects:

public class PartProfile {

private int gameId;
// Some more primitives
private SparseArray<Part> installedParts = new SparseArray<Part>();

// ...
}

public class Part {
   private String partName;
   // More primitives
}

Serialization:

Type genericType = new TypeToken<PartProfile>() {}.getType();
String serializedProfile = Helpers.serializeWithJSON(installedParts, genericType);
preferences.edit().putString("Parts", serializedProfile).commit();

serializeWithJSON():

public static String serializeWithJSON(Object o, Type genericType) {
    Gson gson = new Gson();
    return gson.toJson(o, genericType);
}

Deserialization:

Type genericType = new TypeToken<PartProfile>() {}.getType();
PartProfile parts = gson.fromJson(preferences.getString("Parts", "PARTS_ERROR"), genericType);

SparseArray<Part> retreivedParts = parts.getInstalledParts();
int key;
for (int i = 0; i < retreivedParts.size(); i++) {
    key = retreivedParts.keyAt(i);
    // Exception here:
    Part part = retreivedParts.get(key);
    // ...
}

Exception:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.mypackage.objects.Part

I don't understand why Gson wants to cast a LinkedTreeMap to my object, I never use one in my entire program. I used to have a HashMap<Integer,Part> before I switched to the SparseArray<Part>, and never had issues with that. Are SparseArrays not supported by Gson, or is there an error on my side?

Edit: It seems that the SparseArray gets deserialized correctly, but not the objects inside. Instead of LinkedTreeMaps, these should be of type Part.enter image description here

like image 867
Lennart Avatar asked Jun 21 '13 08:06

Lennart


People also ask

How do you serialize an object using Gson?

Serialization in the context of Gson means converting a Java object to its JSON representation. In order to do the serialization, we need to create the Gson object, which handles the conversion. Next, we need to call the function toJson() and pass the User object. Program output.

Does Gson ignore transient fields?

By default, GSON excludes transient and static fields from the serialization/deserialization process.

What does Gson toJson do?

Gson is the main actor class of Google Gson library. It provides functionalities to convert Java objects to matching JSON constructs and vice versa. Gson is first constructed using GsonBuilder and then toJson(Object) or fromJson(String, Class) methods are used to read/write JSON constructs.


1 Answers

Really there is a way to serialize any kind of SparseArray, here is an example code:

public class SparseArrayTypeAdapter<T> extends TypeAdapter<SparseArray<T>> {

    private final Gson gson = new Gson();
    private final Class<T> classOfT;
    private final Type typeOfSparseArrayOfT = new TypeToken<SparseArray<T>>() {}.getType();
    private final Type typeOfSparseArrayOfObject = new TypeToken<SparseArray<Object>>() {}.getType();

    public SparseArrayTypeAdapter(Class<T> classOfT) {
        this.classOfT = classOfT;
    }

    @Override
    public void write(JsonWriter jsonWriter, SparseArray<T> tSparseArray) throws IOException {
        if (tSparseArray == null) {
            jsonWriter.nullValue();
            return;
        }
        gson.toJson(gson.toJsonTree(tSparseArray, typeOfSparseArrayOfT), jsonWriter);
    }

    @Override
    public SparseArray<T> read(JsonReader jsonReader) throws IOException {
        if (jsonReader.peek() == JsonToken.NULL) {
            jsonReader.nextNull();
            return null;
        }
        SparseArray<Object> temp = gson.fromJson(jsonReader, typeOfSparseArrayOfObject);
        SparseArray<T> result = new SparseArray<T>(temp.size());
        int key;
        JsonElement tElement;
        for (int i = 0; i < temp.size(); i++) {
            key = temp.keyAt(i);
            tElement = gson.toJsonTree(temp.get(key));
            result.put(key, gson.fromJson(tElement, classOfT));
        }
        return result;
    }

}

and to use it you need to register it in your Gson object, like this:

Type sparseArrayType = new TypeToken<SparseArray<MyCustomClass>>() {}.getType();
Gson gson = new GsonBuilder()
    .registerTypeAdapter(sparseArrayType, new SparseArrayTypeAdapter<MyCustomClass>(MyCustomClass.class))
    .create();

you can find this example in this gist.

P.S.: I know it's not optimized at all, but it's only an example to give an idea on how to achieve what you need.

like image 167
dmarcato Avatar answered Oct 24 '22 23:10

dmarcato