用GSON反序列化为ImmutableMap [英] Deserialize to an ImmutableMap with GSON

查看:324
本文介绍了用GSON反序列化为ImmutableMap的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 starterItems:{
Appeltaart: 3,
Soap_50:3
}

...变成Guava 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 {
返回ImmutableMap.copyOf((Map)context.deserialize(json,Map.class));
}
});

但正如所料,这太简单了(没有类型信息)。我得到错误:

  com.google.gson.JsonParseException:JsonDeserializer MapTypeAdapter无法反序列化json object {Appeltaart: 3,Soap_50:3}给定类型接口java.util.Map 

我可以做什么我想要吗?

解决方案

这不是那么直截了当,因为您想要维护类型参数以构建包含正确的类型。要做到这一点,你可以使用 TypeAdapterFactory ,然后请求代理 TypeAdapter ,使用完全指定的 TypeToken

  public class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory {

public static final ImmutableMapTypeAdapterFactory INSTANCE = new ImmutableMapTypeAdapterFactory();

@Override
public< T> TypeAdapter< T>创建(Gson gson,TypeToken< T>类型){
if(!ImmutableMap.class.isAssignableFrom(type.getRawType())){
return null;
}
final TypeAdapter< T> delegate = gson.getDelegateAdapter(this,type);
返回新的TypeAdapter< T>(){
@Override
public void write(JsonWriter out,T value)throws IOException {
delegate.write(out,value);

$ b @Override
@SuppressWarnings(unchecked)
public T read(JsonReader in)throws IOException {
return(T)ImmutableMap。 copyOf((Map)delegate.read(in));
}
};






$ b现在你还有另一个问题:默认的GSON MapTypeAdapterFactory 将尝试创建一个 ImmutableMap 的实例,并对其进行修改。这显然是行不通的。您应该从 TypeToken< ImmutableMap< K,V>> TypeToken< HashMap< K,V>> c>,但我真的不知道你是如何做到的。相反,当 ImmutableMap 时,可以使用 InstanceCreator 来欺骗GSON构建 HashMap / code>实际上是必需的:

  public static  InstanceCreator< Map< K,V>> newCreator(){
return new InstanceCreator< Map< K,V>>(){
@Override
public Map< K,V> createInstance(Type type){
返回新的HashMap< K,V>();
}
};

显然,您必须同时注册TypeAdapterFactory和InstanceCreator:

  GsonBuilder b = new GsonBuilder(); 
b.registerTypeAdapterFactory(new ImmutableMapTypeAdapterFactory());
b.registerTypeAdapter(ImmutableMap.class,ImmutableMapTypeAdapterFactory.newCreator());


I'd like to use GSON to derialize:

"starterItems": {
    "Appeltaart": 3,
    "Soap_50": 3
}

...into a Guava ImmutableMap:

private ImmutableMap<String,Integer> starterItems;

I thought I'd just use regular GSON map parsing, then make an immutable copy of the result, like this:

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

But as expected, that was too simple (there is no type information). I get the error:

com.google.gson.JsonParseException: The JsonDeserializer MapTypeAdapter failed to deserialized json object {"Appeltaart":3,"Soap_50":3} given the type interface java.util.Map

Can I do what I want?

解决方案

It's not that straightforward, since you would want to maintain the type parameters in order to build a map containing the right types. To do that, you can go with a TypeAdapterFactory, and ask there for a delegate TypeAdapter, using the fully specified 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));
            }
        };
    }
}

Now you have another problem: the default GSON MapTypeAdapterFactory will try to create an instance of ImmutableMap, and modify it. This obviously won't work. You should create a TypeToken<HashMap<K, V>> from the TypeToken<ImmutableMap<K, V>>, but I honestly don't know how you can do that. Instead, you can use an InstanceCreator to trick GSON into building a HashMap when an ImmutableMap is actually required:

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

Clearly you have to register both the TypeAdapterFactory and the InstanceCreator:

GsonBuilder b = new GsonBuilder();
b.registerTypeAdapterFactory(new ImmutableMapTypeAdapterFactory());
b.registerTypeAdapter(ImmutableMap.class, ImmutableMapTypeAdapterFactory.newCreator());

这篇关于用GSON反序列化为ImmutableMap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆