我有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,...]),并且重复了{{1}的字符串在multiset中。
当然,我可以将它转换为ArrayList,然后使用count > 1
构造函数来获取我的multiset,但它似乎是一种迂回的方式。是否有更直接的方法来反序列化json对象以检索我的多重集?
答案 0 :(得分:3)
编辑:在这种情况下,有一种更简单的方法可以解决这个问题,即为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
以获取类型参数(在您的示例中为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。