编辑:在这种情况下,似乎有一种更简单的方法可以解决这个问题,即为 Multisets 注册一个 instance creator:
private static class MultisetInstanceCreator implements InstanceCreator<Multiset<?>> {
@Override
public Multiset<?> createInstance(Type type) {
return HashMultiset.create();
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(Multiset.class, new MultisetInstanceCreator())
.create();
实例创建者只是定义了应该如何创建 Multiset,因为 Guava 集合没有默认的构造函数(而且 Multiset 是接口)。
原始答案:我不确定这是否是实现您想要的最佳或最简单的方法,但这是最近对我们解决类似问题的一种方法(在我们的案例中)它正在反序列化为 ImmutableMap)。
基本的想法是注册一个custom deserialiser,它基本上做了你已经发现的可能的解决方案:反序列化为一个ArrayList,然后将它变成一个Multiset。这样做的好处是您只需注册一次自定义反序列化器,而不必知道第一次使用 ArrayList 类型。
此自定义反序列化器如下所示:
private static class MultisetDeserializer implements JsonDeserializer<Multiset<?>> {
@Override
public Multiset<?> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] typeArguments = parameterizedType.getActualTypeArguments();
ParameterizedType listType = new ListParameterizedType(typeArguments);
List<?> list = context.deserialize(json, listType);
return HashMultiset.create(list);
}
}
简而言之,这会将要反序列化的预期类型(例如,在您的情况下为 new TypeToken<Multiset<String>>(){}.getType())转换为 ParameterizedType 以获取类型参数(在您的示例中为字符串)。然后它创建一个新的 ParameterizedType,它是具有相同类型参数的 ArrayList 的类型(如下所示)。在使用上下文将 JSON 反序列化为这种新类型后,您所要做的就是调用 HashMultiset.create。
ListParameterizedType 看起来像这样:
private static class ListParameterizedType implements ParameterizedType {
private final Type[] typeArguments;
private ListParameterizedType(Type[] typeArguments) {
this.typeArguments = typeArguments;
}
@Override
public Type[] getActualTypeArguments() {
return typeArguments;
}
@Override
public Type getRawType() {
return ArrayList.class;
}
@Override
public Type getOwnerType() {
return null;
}
}
请注意,您可以在这里用几乎任何列表类替换 ArrayList,只要它有一个类型参数和一个默认构造函数。
也可能有更简单的方法来实现相同的目标,例如,您可以通过检查 JsonElement 及其 isJsonArray() 等方法来手动进行一些解析。这可以避免您创建一个 ArrayList,然后您会立即将其丢弃。