使用GSON反序列化为ImmutableMap

Bar*_*lom 10 java serialization gson

我想用GSON来推导:

"starterItems": {
    "Appeltaart": 3,
    "Soap_50": 3
}
Run Code Online (Sandbox Code Playgroud)

...进入番石榴ImmutableMap:

private ImmutableMap<String,Integer> starterItems;
Run Code Online (Sandbox Code Playgroud)

我以为我只是使用常规的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));
        }
    });
Run Code Online (Sandbox Code Playgroud)

但正如预期的那样,这太简单了(没有类型信息).我收到错误:

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

我能做我想做的事吗?

Fla*_*vio 7

这并不是那么简单,因为您需要维护类型参数以构建包含正确类型的映射.要做到这一点,你可以使用完全指定的a TypeAdapterFactory,并在那里要求代表.TypeAdapterTypeToken

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));
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您还有另一个问题:默认的GSON MapTypeAdapterFactory将尝试创建ImmutableMap并修改它的实例.这显然是行不通的.你应该创建一个TypeToken<HashMap<K, V>>TypeToken<ImmutableMap<K, V>>,但老实说,我不知道你怎么能做到这一点.相反,你可以使用InstanceCreator欺骗GSON到建设HashMapImmutableMap的实际需要:

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>();
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

GsonBuilder b = new GsonBuilder();
b.registerTypeAdapterFactory(new ImmutableMapTypeAdapterFactory());
b.registerTypeAdapter(ImmutableMap.class, ImmutableMapTypeAdapterFactory.newCreator());
Run Code Online (Sandbox Code Playgroud)


kev*_*rpe 5

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);
      }
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 该类移至 https://github.com/google/caliper/blob/master/caliper-util/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java (2认同)
  • 我喜欢他们使用委托适配器的方法。我认为使用 LinkedHashMap 来保持顺序会更好。 (2认同)