java - Make GSON accept single objects where it expects arrays -
i have bunch of model classes have fields of type list<x>
x
1 of many things (e.g. string
, integer
, of own types). i'm using gson parse json represenations of these models. problem server i'm deal (which beyond control) somehow removed singleton arrays , replaces them contained object. example, instead of returning:
{ "foo": [ "bar"], "bleh": [ { "some": "object" } ] }
it returns:
{ "foo": "bar", "bleh": { "some": "object" } }
now assume java model class this:
public class model { private list<string> foo; private list<someobject> bleh; }
currently causes gson throw exception because finds begin_string
or begin_object
expects begin_array
.
for arrays or lists of strings solved using typeadapter<list<string>>
. problem have list
s many different element types , don't want write separate typeadapter
each case. nor have been able generic typeadapter<list<?>>
, because @ point need know type. there way configure gson smart enough turn single objects or values arrays/lists? or in other words, "pretend" [
, ]
there expects find them doesn't?
but problem have lists many different element types , don't want write separate typeadapter each case. nor have been able generic typeadapter>, because @ point need know type.
this type adapter factories designed for: can control every type in gson
instance configuration.
final class alwayslisttypeadapterfactory<e> implements typeadapterfactory { // gson can instantiate private alwayslisttypeadapterfactory() { } @override public <t> typeadapter<t> create(final gson gson, final typetoken<t> typetoken) { // if it's not list -- delegate job gson , let pick best type adapter if ( !list.class.isassignablefrom(typetoken.getrawtype()) ) { return null; } // resolving list parameter type final type elementtype = resolvetypeargument(typetoken.gettype()); @suppresswarnings("unchecked") final typeadapter<e> elementtypeadapter = (typeadapter<e>) gson.getadapter(typetoken.get(elementtype)); // note always-list type adapter made null-safe, don't have check nulls ourselves @suppresswarnings("unchecked") final typeadapter<t> alwayslisttypeadapter = (typeadapter<t>) new alwayslisttypeadapter<>(elementtypeadapter).nullsafe(); return alwayslisttypeadapter; } private static type resolvetypeargument(final type type) { // given type not parameterized? if ( !(type instanceof parameterizedtype) ) { // no, raw return object.class; } final parameterizedtype parameterizedtype = (parameterizedtype) type; return parameterizedtype.getactualtypearguments()[0]; } private static final class alwayslisttypeadapter<e> extends typeadapter<list<e>> { private final typeadapter<e> elementtypeadapter; private alwayslisttypeadapter(final typeadapter<e> elementtypeadapter) { this.elementtypeadapter = elementtypeadapter; } @override public void write(final jsonwriter out, final list<e> list) { throw new unsupportedoperationexception(); } @override public list<e> read(final jsonreader in) throws ioexception { // detect list "type" final list<e> list = new arraylist<>(); final jsontoken token = in.peek(); switch ( token ) { case begin_array: // if it's regular list, consume [, <all elements>, , ] in.beginarray(); while ( in.hasnext() ) { list.add(elementtypeadapter.read(in)); } in.endarray(); break; case begin_object: case string: case number: case boolean: // object or primitive? add current value result list list.add(elementtypeadapter.read(in)); break; case null: throw new assertionerror("must never happen: check if type adapter configured .nullsafe()"); case name: case end_array: case end_object: case end_document: throw new malformedjsonexception("unexpected token: " + token); default: throw new assertionerror("must never happen: " + token); } return list; } } }
now have tell gson which fields not well-formed. of course, might configure whole gson
instance accept such lists, let more precise using @jsonadapter
annotation:
final class model { @jsonadapter(alwayslisttypeadapterfactory.class) final list<string> foo = null; @jsonadapter(alwayslisttypeadapterfactory.class) final list<someobject> bleh = null; @override public string tostring() { return "model{" + "foo=" + foo + ", bleh=" + bleh + '}'; } } final class someobject { final string = null; @override public string tostring() { return "someobject{" + "some='" + + '\'' + '}'; } }
test data:
single.json
{ "foo": "bar", "bleh": {"some": "object"} }
list.json
{ "foo": ["bar"], "bleh": [{"some": "object"}] }
example:
private static final gson gson = new gson(); public static void main(final string... args) throws ioexception { ( final string resource : immutablelist.of("single.json", "list.json") ) { try ( final jsonreader jsonreader = getpackageresourcejsonreader(q43412261.class, resource) ) { final model model = gson.fromjson(jsonreader, model.class); system.out.println(model); } } }
and output:
model{foo=[bar], bleh=[someobject{some='object'}]}
model{foo=[bar], bleh=[someobject{some='object'}]}
Comments
Post a Comment