我想序列化一个自定义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正确反序列化,但不是内部的对象。这些应该是LinkedTreeMaps
。
Part
答案 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