serialization - Create a Gson TypeAdapter for a Guava Range -
i trying serialize guava range objects json using gson, default serialization fails, , i'm unsure how correctly implement typeadapter
generic type.
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);
this fails so:
{"lowerbound":{"endpoint":10},"upperbound":{"endpoint":20}} passed: typetokeninterface failed: range java.lang.runtimeexception: unable invoke no-args constructor com.google.common.collect.cut<java.lang.integer>. register instancecreator gson type may fix problem. @ com.google.gson.internal.constructorconstructor$12.construct( constructorconstructor.java:210) ...
note default serialization loses information - fails report whether endpoints open or closed. prefer see serialized similar tostring()
, e.g. [10‥20]
calling tostring()
won't work generic range
instances, elements of range may not primitives (joda-time localdate
instances, example). same reason, implementing custom typeadapter
seems difficult, don't know how deserialize endpoints.
i've implemented of typeadaptorfactory
based on template provided multimap
ought work, i'm stuck on generics. here's have far:
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: generic method newrangeadapter(typeadapter<e>) of type // gsonutils.rangetypeadapterfactory not applicable arguments // (typeadapter<capture#4-of ?>). inferred type capture#4-of ? not valid // substitute 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); } }; } }
however return (typeadapter<t>) newrangeadapter(elementadapter);
line has compilation error , i'm @ loss.
what's best way resolve error? there better way serialize range
objects i'm missing? if want serialize rangeset
s?
rather frustrating google utility library , google serialization library seem require glue work :(
this feels reinventing wheel, lot quicker put , test time spent trying gson behave, @ least presently i'll using following converter
s serialize range
, rangeset
*, rather gson.
/** * converter between range instances , strings, custom serializer. * ideally we'd let gson or guava us, presently 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 , strings, custom serializer. * ideally we'd let gson or guava us, presently 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(); } }; }
*for inter-process communication, java serialization work fine, both classes implement serializable
. i'm serializing disk more permanent storage, meaning need format can trust won't change on time. guava's serialization doesn't provide guarantee.
Comments
Post a Comment