我有Multiset<String>
对象,我将其序列化为Json。我正在使用Gson进行以下操作:
Multiset<String> mset = ... ;
Gson gson = new Gson();
Files.write(Gson.toJson(mset), new File(abosulte_path_string), Charset.defaultCharset());
当我尝试对其进行反序列化时,请执行以下操作:
String json_string = ... // read from file
Type type = new TypeToken<Multiset<String>>(){}.getType();
Multiset<String> treated = gson.fromJson(json_string, type);
我收到此错误:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.google.common.collect.Multiset
当我打开json文件时,我看到
Multiset<String>
对象确实被表示为ArrayList([string1,string2,...]),并重复了多集中具有count > 1
的字符串。我当然可以将其转换为ArrayList,然后使用
create(Iterable<>)
构造函数获取我的多重集,但这似乎是一种回旋方式。有没有更直接的方法来反序列化json对象以检索我的多集? 最佳答案
编辑:在这种情况下,似乎有一种更简单的方法来解决此问题,即通过为多集注册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
以获取类型参数(您的示例中为String)。然后,它创建一个新的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,然后立即丢弃它。