我有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,…])表示的,重复计数的计数大于等于2的字符串.多重集中的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,只要它具有一个类型参数和一个默认构造函数即可.
也可能有更简单的方法来实现相同的目的,例如,您可以通过使用isJsonArray()之类的方法检查JsonElement来手动进行一些解析.这样可以避免创建ArrayList,然后立即丢弃它.