EDIT: It looks like in this case there is a much easier way to solve this problem, namely by registering an instance creator for Multisets:
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();
The instance creator just defines how a Multiset should be created, since the Guava collections don't have default constructors (and Multiset is the interface anyway).
Original answer: I'm not sure if this is the best or easiest way to achieve what you want, but it is one way that worked for us recently for a similar problem (in our case it was deserialising to an ImmutableMap).
The basic idea is to register a custom deserialiser which basically does what you already discovered as a possible solution: deserialise to an ArrayList, then turn it into a Multiset. The advantage here is that you only have to do register the custom deserialiser once, rather than having to know everywhere to first use an ArrayList type.
This custom deserialiser looks like this:
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);
}
}
In short, this casts the expected type to deserialise to (e.g. new TypeToken<Multiset<String>>(){}.getType()
in your case) to ParameterizedType
to get at the type arguments (String in your example). It then creates a new ParameterizedType which is the type of an ArrayList with the same type arguments (shown below). After using the context to deserialise the JSON to this new type, all you have to do is call HashMultiset.create
.
The ListParameterizedType
looks like this:
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;
}
}
Note that you could replace ArrayList
with pretty much any list class here, as long as it has one type argument and a default constructor.
There may also be easier ways to achieve the same thing, for example you could do some of the parsing manually by inspecting the JsonElement
with its methods like isJsonArray()
. This could save you from creating an ArrayList which you throw away immediately afterwards.