我想使用Gson將Guava Range對象序列化爲JSON,但默認序列化失敗,並且我不確定如何正確實現此泛型類型的TypeAdapter
。爲Guava範圍創建一個Gson TypeAdapter
Gson gson = new Gson();
Range<Integer> range = Range.closed(10, 20);
String json = gson.toJson(range);
System.out.println(json);
Range<Integer> range2 = gson.fromJson(json,
new TypeToken<Range<Integer>>(){}.getType());
System.out.println(range2);
assertEquals(range2, range);
這種失敗,像這樣:
{"lowerBound":{"endpoint":10},"upperBound":{"endpoint":20}}
PASSED: typeTokenInterface
FAILED: range
java.lang.RuntimeException: Unable to invoke no-args constructor for
com.google.common.collect.Cut<java.lang.Integer>. Register an
InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$12.construct(
ConstructorConstructor.java:210)
...
注意,缺省的序列實際上丟失信息 - 它沒有報告端點是否打開或關閉。我希望看到它的序列化類似於它的toString()
,例如,然而,簡單地調用toString()
將不會與通用Range
實例一起使用,因爲範圍的元素可能不是原語(例如,Joda-Time LocalDate
實例)。出於同樣的原因,實現自定義TypeAdapter
似乎很難,因爲我們不知道如何反序列化端點。
我已經實施了大部分TypeAdaptorFactory
的基礎上爲Multimap
提供的模板應該工作,但現在我卡在泛型。這是我到目前爲止有:
public class RangeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Range.class
|| !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = (TypeAdapter<?>)gson.getAdapter(TypeToken.get(elementType));
// Bound mismatch: The generic method newRangeAdapter(TypeAdapter<E>) of type
// GsonUtils.RangeTypeAdapterFactory is not applicable for the arguments
// (TypeAdapter<capture#4-of ?>). The inferred type capture#4-of ? is not a valid
// substitute for the bounded parameter <E extends Comparable<?>>
return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
}
private <E extends Comparable<?>> TypeAdapter<Range<E>> newRangeAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Range<E>>() {
@Override
public void write(JsonWriter out, Range<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String repr = (value.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(value.hasLowerBound() ? elementAdapter.toJson(value.lowerEndpoint()) : "-\u221e") +
'\u2025' +
(value.hasLowerBound() ? elementAdapter.toJson(value.upperEndpoint()) : "+\u221e") +
(value.upperBoundType() == BoundType.CLOSED ? "]" : ")");
out.value(repr);
}
public Range<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String[] endpoints = in.nextString().split("\u2025");
E lower = elementAdapter.fromJson(endpoints[0].substring(1));
E upper = elementAdapter.fromJson(endpoints[1].substring(0,endpoints[1].length()-1));
return Range.range(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN,
upper, endpoints[1].charAt(endpoints[1].length()-1) == '[' ? BoundType.CLOSED : BoundType.OPEN);
}
};
}
}
然而return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
線具有編譯錯誤,我現在處於虧損狀態來的。
解決此錯誤的最佳方法是什麼?有沒有更好的方法來序列化我錯過的對象Range
?如果我想序列化RangeSet
s呢?
而不是沮喪的是,谷歌工具庫和谷歌序列化庫似乎需要這麼多膠共同努力:(
我想這個問題是混淆了'T'和範圍''。其實,我在你的代碼中看不到後者,但是'TypeAdapter >'是恕我直言,工廠應該返回。 –
maaartinus
@maaartinus是的,複雜的泛型是這個問題的重要組成部分。 'T'是指Gson要處理的類型(在這種情況下,'RangeSet'),所以'TypeAdapter '應該是正確的,但爲了處理'RangeSet ',我需要能夠處理'C's,'TypeAdaptor'並不真正可以訪問。我試圖複製['TypeAdaptorFactory'](http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/TypeAdapterFactory.html)中的'Multiset'示例,但我不確定這是可能的。 –
dimo414
我看到我完全錯了。我想你的'elementAdapter'必須是'TypeAdapter>',但我不知道如果沒有骯髒的黑客攻擊是可能的。當首先投射到「對象」時,可以將任何東西投射到任何東西上。當然,它是不安全和骯髒的,但有時它是唯一的方法。在獲得黑客工作後,我經常找到更好的解決方案。 –
maaartinus