Serializing A Sparsearray With Gson
Solution 1:
Really there is a way to serialize any kind of SparseArray
, here is an example code:
publicclassSparseArrayTypeAdapter<T> extendsTypeAdapter<SparseArray<T>> {
privatefinalGsongson=newGson();
privatefinal Class<T> classOfT;
privatefinalTypetypeOfSparseArrayOfT=newTypeToken<SparseArray<T>>() {}.getType();
privatefinalTypetypeOfSparseArrayOfObject=newTypeToken<SparseArray<Object>>() {}.getType();
publicSparseArrayTypeAdapter(Class<T> classOfT) {
this.classOfT = classOfT;
}
@Overridepublicvoidwrite(JsonWriter jsonWriter, SparseArray<T> tSparseArray)throws IOException {
if (tSparseArray == null) {
jsonWriter.nullValue();
return;
}
gson.toJson(gson.toJsonTree(tSparseArray, typeOfSparseArrayOfT), jsonWriter);
}
@Overridepublic SparseArray<T> read(JsonReader jsonReader)throws IOException {
if (jsonReader.peek() == JsonToken.NULL) {
jsonReader.nextNull();
returnnull;
}
SparseArray<Object> temp = gson.fromJson(jsonReader, typeOfSparseArrayOfObject);
SparseArray<T> result = newSparseArray<T>(temp.size());
int key;
JsonElement tElement;
for (inti=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:
TypesparseArrayType=newTypeToken<SparseArray<MyCustomClass>>() {}.getType();
Gsongson=newGsonBuilder()
.registerTypeAdapter(sparseArrayType, newSparseArrayTypeAdapter<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.
Solution 2:
It seems that the SparseArray gets deserialized correctly, but not the objects inside. Instead of LinkedTreeMaps, these should be of type Part.
Your observation is correct, since SparseArray contains Object (not Part
), Gson won't have any clue to make Part as your object type. Hence it map your list as its infamous internal type LinkedTreeMap
.
To solve it, I think you won't be able to use SparseArray
... Or you may try retreivedParts.get(key).toString()
, then use gson to parse the object again. But I don't think it's efficient to do that
Solution 3:
As pointed out in the other answers SparseArray
's internal implementation uses an Object[]
to store the values so Gson cannot deserialize it correctly.
This can be solved by creating a custom Gson TypeAdapterFactory
:
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import android.util.SparseArray;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
publicclassSparseArrayTypeAdapterFactoryimplementsTypeAdapterFactory {
publicstaticfinalSparseArrayTypeAdapterFactoryINSTANCE=newSparseArrayTypeAdapterFactory();
privateSparseArrayTypeAdapterFactory() { }
@Overridepublic <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// This factory only supports (de-)serializing SparseArrayif (type.getRawType() != SparseArray.class) {
returnnull;
}
// Get the type argument for the element type parameter `<E>`// Note: Does not support raw SparseArray type (i.e. without type argument)TypeelementType= ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
// This is safe because check at the beginning made sure type is SparseArray@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) newSparseArrayTypeAdapter<>(elementAdapter);
// call nullSafe() to make adapter automatically handle `null` SparseArraysreturn adapter.nullSafe();
}
privatestaticclassSparseArrayTypeAdapter<E> extendsTypeAdapter<SparseArray<E>> {
privatefinal TypeAdapter<E> elementTypeAdapter;
publicSparseArrayTypeAdapter(TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
@Overridepublicvoidwrite(JsonWriter out, SparseArray<E> sparseArray)throws IOException {
out.beginObject();
intsize= sparseArray.size();
for (inti=0; i < size; i++) {
out.name(Integer.toString(sparseArray.keyAt(i)));
elementTypeAdapter.write(out, sparseArray.valueAt(i));
}
out.endObject();
}
@Overridepublic SparseArray<E> read(JsonReader in)throws IOException {
in.beginObject();
SparseArray<E> sparseArray = newSparseArray<>();
while (in.hasNext()) {
intkey= Integer.parseInt(in.nextName());
Evalue= elementTypeAdapter.read(in);
// Use `append(...)` here because SparseArray is serialized in ascending// key order so `key` will be > previously added key
sparseArray.append(key, value);
}
in.endObject();
return sparseArray;
}
}
}
This factory serializes SparseArrays as JSON objects with the key as JSON property name and the value serialized with the respective adapter as JSON value, e.g.:
newSparseArray<List<String>>().put(5, Arrays.asList("Hello", "World"))
↓ JSON
{"5":["Hello","World"]}
You then use this TypeAdapterFactory by creating your Gson instance using a GsonBuilder
on which you register the TypeAdapterFactory:
Gsongson=newGsonBuilder()
.registerTypeAdapterFactory(SparseArrayTypeAdapterFactory.INSTANCE)
.create();
Post a Comment for "Serializing A Sparsearray With Gson"