我想用GSON来推广:
"starterItems": {
"Appeltaart": 3,
"Soap_50": 3
}
...进入番石榴ImmutableMap
:
private ImmutableMap<String,Integer> starterItems;
我以为我只是使用常规的GSON映射解析,然后制作结果的不可变副本,如下所示:
gb.registerTypeAdapter(ImmutableMap.class, new JsonDeserializer<ImmutableMap>() {
@SuppressWarnings("unchecked")
@Override public ImmutableMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return ImmutableMap.copyOf((Map) context.deserialize(json, Map.class));
}
});
但正如预期的那样,这太简单了(没有类型信息)。我收到错误:
com.google.gson.JsonParseException: The JsonDeserializer MapTypeAdapter failed to deserialized json object {"Appeltaart":3,"Soap_50":3} given the type interface java.util.Map
我能做我想做的事吗?
答案 0 :(得分:7)
这并不是那么简单,因为您需要维护类型参数以构建包含正确类型的地图。为此,您可以使用TypeAdapterFactory
,并使用完全指定的TypeAdapter
向其请求代理人TypeToken
。
public class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory {
public static final ImmutableMapTypeAdapterFactory INSTANCE = new ImmutableMapTypeAdapterFactory();
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!ImmutableMap.class.isAssignableFrom(type.getRawType())) {
return null;
}
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
@Override
@SuppressWarnings("unchecked")
public T read(JsonReader in) throws IOException {
return (T) ImmutableMap.copyOf((Map) delegate.read(in));
}
};
}
}
现在您还有另一个问题:默认的GSON MapTypeAdapterFactory
将尝试创建ImmutableMap
的实例,并对其进行修改。这显然是行不通的。您应该从TypeToken<HashMap<K, V>>
创建TypeToken<ImmutableMap<K, V>>
,但老实说我不知道如何做到这一点。相反,当实际需要InstanceCreator
时,您可以使用HashMap
来诱骗GSON构建ImmutableMap
:
public static <K,V> InstanceCreator<Map<K, V>> newCreator() {
return new InstanceCreator<Map<K, V>>() {
@Override
public Map<K, V> createInstance(Type type) {
return new HashMap<K, V>();
}
};
}
显然,您必须同时注册TypeAdapterFactory和InstanceCreator:
GsonBuilder b = new GsonBuilder();
b.registerTypeAdapterFactory(new ImmutableMapTypeAdapterFactory());
b.registerTypeAdapter(ImmutableMap.class, ImmutableMapTypeAdapterFactory.newCreator());
答案 1 :(得分:2)
这也适用于ImmutableMap:https://stackoverflow.com/a/7773201/342947
public final static class ImmutableMapDeserializer implements JsonDeserializer<ImmutableMap<?,?>> {
@Override
public ImmutableMap<?,?> deserialize(final JsonElement json, final Type type,
final JsonDeserializationContext context) throws JsonParseException
{
final Type type2 =
ParameterizedTypeImpl.make(Map.class, ((ParameterizedType) type).getActualTypeArguments(), null);
final Map<?,?> map = context.deserialize(json, type2);
return ImmutableMap.copyOf(map);
}
}
gBuilder.registerTypeAdapter(ImmutableMap.class, new GsonUtils.ImmutableMapDeserializer());
答案 2 :(得分:1)
当您添加所需的类型信息时,它会起作用:
gb.registerTypeAdapter(
new TypeToken<ImmutableMap<String,Integer>>(){}.getType(),
new JsonDeserializer<ImmutableMap>() {
@SuppressWarnings("unchecked")
@Override
public ImmutableMap deserialize(JsonElement json,
Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
return ImmutableMap.copyOf(
(Map) context.deserialize(json, new TypeToken<Map<String,Integer>>(){}.getType()));
}
});
但是,这需要为应支持的所有类型的地图复制该代码。在这方面,弗拉维奥的答案可能更为通用。
答案 3 :(得分:1)
Just基于Flavio的答案,但是采用快速的两步复制和粘贴格式。
创建GsonUtils
课程(我已在下面提及)
将new Gson()
的所有实例替换为GsonUtils.get()
GsonUtils.java :
package com.innomatixdata.busscan.utils;
import android.support.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
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;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
/**
* Use this class to get an instance of Gson that is configured for this application.
*/
public class GsonUtils {
private static final GsonBuilder GSON_BUILDER = new GsonBuilder();
static {
GSON_BUILDER.registerTypeAdapterFactory(new ImmutableMapTypeAdapterFactory());
GSON_BUILDER.registerTypeAdapter(ImmutableMap.class, ImmutableMapTypeAdapterFactory.newCreator());
}
public static Gson get() {
return GSON_BUILDER.create();
}
public static class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory {
@Nullable
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!ImmutableMap.class.isAssignableFrom(type.getRawType())) {
return null;
}
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
@Override
@SuppressWarnings("unchecked")
public T read(JsonReader in) throws IOException {
return (T) ImmutableMap.copyOf((Map) delegate.read(in));
}
};
}
public static <K,V> InstanceCreator<Map<K, V>> newCreator() {
return new InstanceCreator<Map<K, V>>() {
@Override
public Map<K, V> createInstance(Type type) {
return new HashMap<K, V>();
}
};
}
}
}
答案 4 :(得分:1)
Google的Caliper项目(Java的微型基准测试程序)有一个: https://github.com/google/caliper/blob/master/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java
参考(如果它移动,或将来删除):
/**
* Serializes and deserializes {@link ImmutableMap} instances using a {@link HashMap} as an
* intermediary.
*/
final class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != ImmutableMap.class
|| !(type instanceof ParameterizedType)) {
return null;
}
com.google.common.reflect.TypeToken<ImmutableMap<?, ?>> betterToken =
(com.google.common.reflect.TypeToken<ImmutableMap<?, ?>>)
com.google.common.reflect.TypeToken.of(typeToken.getType());
final TypeAdapter<HashMap<?, ?>> hashMapAdapter =
(TypeAdapter<HashMap<?, ?>>) gson.getAdapter(
TypeToken.get(betterToken.getSupertype(Map.class).getSubtype(HashMap.class)
.getType()));
return new TypeAdapter<T>() {
@Override public void write(JsonWriter out, T value) throws IOException {
HashMap<?, ?> hashMap = Maps.newHashMap((Map<?, ?>) value);
hashMapAdapter.write(out, hashMap);
}
@Override public T read(JsonReader in) throws IOException {
HashMap<?, ?> hashMap = hashMapAdapter.read(in);
return (T) ImmutableMap.copyOf(hashMap);
}
};
}
}
答案 5 :(得分:0)
要添加Flavio的答案,可以更改传递给getDelegateAdapter()
的TypeToken的原始类型,从而不需要InstanceCreator。
final TypeToken<T> mapType = (TypeToken<T>) TypeToken.getParameterized(Map.class,
((ParameterizedType) type.getType()).getActualTypeArguments());
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, mapType);