为番石榴范围创建Gson TypeAdapter

问题描述 投票:1回答:3

我正在尝试使用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似乎很困难,因为我们不知道如何对端点进行反序列化。

我已经基于为TypeAdaptorFactory提供的模板实现了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,该怎么办?

令人沮丧的是,Google实用程序库和Google序列化库似乎需要太多胶水才能一起工作:(

serialization range gson guava rangeset
3个回答
1
投票

感觉上有点像在重新发明轮子,但是比起使Gson表现出来所花费的时间,它的组装和测试要快得多,因此至少目前我将使用以下RangeSet进行序列化[ C0]和Converter *,而不是Gson。

Converter

*对于进程间通信,由于两个类都实现了Range,因此Java序列化可能会正常工作。但是我正在序列化到磁盘上以提供更多永久性存储,这意味着我需要一种可以信赖的格式,它不会随着时间的推移而改变。番石榴的序列化RangeSet


1
投票

这里是Gson JsonSerializer和JsonDeserializer,它们通常支持Range:/** * Converter between Range instances and Strings, essentially a custom serializer. * Ideally we'd let Gson or Guava do this for us, but presently this is cleaner. */ public static <T extends Comparable<? super T>> Converter<Range<T>, String> rangeConverter(final Converter<T, String> elementConverter) { final String NEG_INFINITY = "-\u221e"; final String POS_INFINITY = "+\u221e"; final String DOTDOT = "\u2025"; return new Converter<Range<T>, String>() { @Override protected String doForward(Range<T> range) { return (range.hasLowerBound() && range.lowerBoundType() == BoundType.CLOSED ? "[" : "(") + (range.hasLowerBound() ? elementConverter.convert(range.lowerEndpoint()) : NEG_INFINITY) + DOTDOT + (range.hasUpperBound() ? elementConverter.convert(range.upperEndpoint()) : POS_INFINITY) + (range.hasUpperBound() && range.upperBoundType() == BoundType.CLOSED ? "]" : ")"); } @Override protected Range<T> doBackward(String range) { String[] endpoints = range.split(DOTDOT); Range<T> ret = Range.all(); if(!endpoints[0].substring(1).equals(NEG_INFINITY)) { T lower = elementConverter.reverse().convert(endpoints[0].substring(1)); ret = ret.intersection(Range.downTo(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN)); } if(!endpoints[1].substring(0,endpoints[1].length()-1).equals(POS_INFINITY)) { T upper = elementConverter.reverse().convert(endpoints[1].substring(0,endpoints[1].length()-1)); ret = ret.intersection(Range.upTo(upper, endpoints[1].charAt(endpoints[1].length()-1) == ']' ? BoundType.CLOSED : BoundType.OPEN)); } return ret; } }; } /** * Converter between RangeSet instances and Strings, essentially a custom serializer. * Ideally we'd let Gson or Guava do this for us, but presently this is cleaner. */ public static <T extends Comparable<? super T>> Converter<RangeSet<T>, String> rangeSetConverter(final Converter<T, String> elementConverter) { return new Converter<RangeSet<T>, String>() { private final Converter<Range<T>, String> rangeConverter = rangeConverter(elementConverter); @Override protected String doForward(RangeSet<T> rs) { ArrayList<String> ls = new ArrayList<>(); for(Range<T> range : rs.asRanges()) { ls.add(rangeConverter.convert(range)); } return Joiner.on(", ").join(ls); } @Override protected RangeSet<T> doBackward(String rs) { Iterable<String> parts = Splitter.on(",").trimResults().split(rs); ImmutableRangeSet.Builder<T> build = ImmutableRangeSet.builder(); for(String range : parts) { build.add(rangeConverter.reverse().convert(range)); } return build.build(); } }; }

Serializable

0
投票

这里是直接解决方案。效果很好

doesn't provide that guarantee
© www.soinside.com 2019 - 2024. All rights reserved.