【发布时间】:2014-04-20 06:02:21
【问题描述】:
我正在尝试使用 Gson 将 Guava Range 对象序列化为 JSON,但是默认序列化失败,并且我不确定如何正确实现此泛型类型的 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(),例如[10‥20] 然而,简单地调用 toString() 不适用于通用 Range 实例,因为范围的元素可能不是基元(例如,Joda-Time LocalDate 实例)。出于同样的原因,实现自定义 TypeAdapter 似乎很困难,因为我们不知道如何反序列化端点。
我已经根据为Multimap 提供的模板实现了大部分TypeAdaptorFactory,它应该可以工作,但现在我被困在泛型上。到目前为止,这是我所拥有的:
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 对象?如果我想序列化RangeSets呢?
令人沮丧的是,Google 实用程序库和 Google 序列化库似乎需要如此多的粘合剂才能协同工作:(
【问题讨论】:
-
我猜这个问题混淆了
T和Range<T>。实际上,我在您的代码中看不到后者,但TypeAdapter<Range<T>>恕我直言,工厂应该返回。 -
@maaartinus 是的,复杂的泛型是问题的很大一部分。
T指的是 Gson 要处理的类型(在本例中为RangeSet<C>),因此TypeAdapter<T>转换应该是正确的,但为了处理RangeSet<C>我需要能够处理 @987654345 @s,TypeAdaptor并没有真正可以访问。我试图在TypeAdaptorFactory中复制Multiset示例,但我不确定是否可行。 -
我明白我完全错了。我猜你的
elementAdapter一定是TypeAdapter<E extends Comparable<E>>,但我不知道如果没有肮脏的黑客攻击是否可行。当首先投射到Object时,可以将任何东西投射到任何东西上。当然,这是不安全和肮脏的地狱,但有时这是唯一的方法。通常我会在 hack 工作后找到更好的解决方案。
标签: serialization range gson guava rangeset