使用GSON </t>序列化SparseArray <t>

时间:2013-06-21 08:58:20

标签: android serialization sharedpreferences gson

我想序列化一个自定义Java对象,因此我可以使用SharedPreferences来存储它并在另一个Activity中进行检索。我不需要持久存储,SharedPreferences,我在关闭应用程序时擦除它们。我目前正在使用GSON,但它似乎不适用于Android的SparseArray类型。

我的对象:

public class PartProfile {

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

// ...
}

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

序列化:

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);
}

反序列化:

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);
    // ...
}

例外:

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

我不明白为什么Gson想要将LinkedTreeMap投射到我的对象上,我从不在整个程序中使用它。在我切换到HashMap<Integer,Part>之前,我曾经有一个SparseArray<Part>,从来没有遇到过这个问题。 Gson不支持SparseArrays,或者我这边有错误吗?

编辑:似乎SparseArray正确反序列化,但不是内部的对象。这些应该是LinkedTreeMapsenter image description here

,而不是Part

3 个答案:

答案 0 :(得分:13)

确实有一种方法可以序列化任何类型的SparseArray,这是一个示例代码:

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;
    }

}

要使用它,您需要在Gson对象中注册它,如下所示:

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

您可以在this gist中找到此示例。

P.S。:我知道它根本没有优化,但它只是一个例子来说明如何实现你所需要的。

答案 1 :(得分:5)

  

似乎SparseArray正确反序列化,但不是   里面的物体。而不是LinkedTreeMaps,这些应该是类型   部分。

您的观察是正确的,因为 SparseArray包含对象(不是Part),Gson将没有任何线索将Part作为您的对象类型。因此,它会将您的列表映射为臭名昭着的内部类型LinkedTreeMap

要解决它,我认为你将无法使用SparseArray ...或者你可以尝试retreivedParts.get(key).toString(),然后使用gson再次解析对象。但我不认为这样做有效

答案 2 :(得分:0)

如其他答案所指出,import tkinter as tk root = tk.Tk() spinbox = tk.Spinbox(root, values=('red', 'blue', 'green')) spinbox.pack() def new_values(spinbox): spinbox.config(values=('yellow', 'cyan', 'magenta')) chg_btn = tk.Button(root, text="Chg", command=lambda: new_values(spinbox)) chg_btn.pack() root.mainloop() 的{​​{3}}使用SparseArray来存储值,因此Gson无法正确反序列化它。

这可以通过创建自定义Gson internal implementation来解决:

Object[]

该工厂将SparseArrays序列化为JSON对象,其键为JSON属性名称,并将使用相应适配器序列化的值作为JSON值,例如:

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;

public class SparseArrayTypeAdapterFactory implements TypeAdapterFactory {
    public static final SparseArrayTypeAdapterFactory INSTANCE = new SparseArrayTypeAdapterFactory();
    
    private SparseArrayTypeAdapterFactory() { }
    
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // This factory only supports (de-)serializing SparseArray
        if (type.getRawType() != SparseArray.class) {
            return null;
        }
        
        // Get the type argument for the element type parameter `<E>`
        // Note: Does not support raw SparseArray type (i.e. without type argument)
        Type elementType = ((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>) new SparseArrayTypeAdapter<>(elementAdapter);
        // call nullSafe() to make adapter automatically handle `null` SparseArrays
        return adapter.nullSafe();
    }
    
    private static class SparseArrayTypeAdapter<E> extends TypeAdapter<SparseArray<E>> {
        private final TypeAdapter<E> elementTypeAdapter;
        
        public SparseArrayTypeAdapter(TypeAdapter<E> elementTypeAdapter) {
            this.elementTypeAdapter = elementTypeAdapter;
        }
        
        @Override
        public void write(JsonWriter out, SparseArray<E> sparseArray) throws IOException {
            out.beginObject();
            
            int size = sparseArray.size();
            for (int i = 0; i < size; i++) {
                out.name(Integer.toString(sparseArray.keyAt(i)));
                elementTypeAdapter.write(out, sparseArray.valueAt(i));
            }
            
            out.endObject();
        }

        @Override
        public SparseArray<E> read(JsonReader in) throws IOException {
            in.beginObject();
            
            SparseArray<E> sparseArray = new SparseArray<>();
            while (in.hasNext()) {
                int key = Integer.parseInt(in.nextName());
                E value = 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;
        }
        
    }
}

↓JSON

new SparseArray<List<String>>().put(5, Arrays.asList("Hello", "World"))

然后,通过使用TypeAdapterFactory TypeAdapterFactory上的{"5": ["Hello", "World"]} 创建Gson实例来使用此TypeAdapterFactory:

GsonBuilder