I am working on an Android app that uses Retrofit+OkHttp to connect to a REST API and consume JSON data. I\'m fairly new to Retrofit, so I\'m still learning how it works, bu
You seem to have not use List because you have to create at least two kinds of objects: "single" and "multiple" where the latter must implement List to satisfy the array-like interface.
For example. JsonResponse is simpler than what jsonschema2pojo generates:
final class JsonResponse {
final boolean success = Boolean.valueOf(false);
final String message = null;
final List data = null;
}
Now, MyObject can look like this:
abstract class MyObject
implements Parcelable {
private MyObject() {
}
static MyObject multiple(final List list) {
return new MultipleObjects<>(list);
}
static final class SingleObject
extends MyObject {
private SingleObject() {
}
final String info = null;
final String info2 = null;
final String info3 = null;
}
static final class MultipleObjects
extends MyObject
implements List {
private final List list;
private MultipleObjects(final List list) {
this.list = list;
}
// @formatter:off
@Override public int size() { return list.size(); }
@Override public boolean isEmpty() { return list.isEmpty(); }
@Override public boolean contains(final Object o) { return list.contains(o); }
@Override public Iterator iterator() { return list.iterator(); }
@Override public Object[] toArray() { return list.toArray(); }
@Override public T[] toArray(final T[] a) { return list.toArray(a); }
@Override public boolean add(final E e) { return list.add(e); }
@Override public boolean remove(final Object o) { return list.remove(o); }
@Override public boolean containsAll(final Collection> c) { return list.containsAll(c); }
@Override public boolean addAll(final Collection extends E> c) { return list.addAll(c); }
@Override public boolean addAll(final int index, final Collection extends E> c) { return list.addAll(index, c); }
@Override public boolean removeAll(final Collection> c) { return list.removeAll(c); }
@Override public boolean retainAll(final Collection> c) { return list.retainAll(c); }
@Override public void clear() { list.clear(); }
@Override public E get(final int index) { return list.get(index); }
@Override public E set(final int index, final E element) { return list.set(index, element); }
@Override public void add(final int index, final E element) { list.add(index, element); }
@Override public E remove(final int index) { return list.remove(index); }
@Override public int indexOf(final Object o) { return list.indexOf(o); }
@Override public int lastIndexOf(final Object o) { return list.lastIndexOf(o); }
@Override public ListIterator listIterator() { return list.listIterator(); }
@Override public ListIterator listIterator(final int index) { return list.listIterator(index); }
@Override public List subList(final int fromIndex, final int toIndex) { return list.subList(fromIndex, toIndex); }
// @formatter:on
}
}
The class above implements an abstract class that can be implemented in two ways. Note that no public constructors are exposed by design: SingleObject can be deserialized using Gson really easy using the reflective strategy, whilst MultipleObjects is an array-like object that requires some manual construction.
The deserialization part:
final class MyObjectJsonDeserializer
implements JsonDeserializer {
private static final JsonDeserializer myObjectJsonDeserializer = new MyObjectJsonDeserializer();
// You have to detect it more accurately yourself
private static final Type genericListType = new TypeToken>() {
}.getType();
private MyObjectJsonDeserializer() {
}
static JsonDeserializer getMyObjectJsonDeserializer() {
return myObjectJsonDeserializer;
}
@Override
public MyObject deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
if ( jsonElement.isJsonNull() ) {
return null;
}
if ( jsonElement.isJsonObject() ) {
// Note that the deserialization is implemented using context,
// because it makes sure that you are using the Gson instance configuration
// Simply speaking: do not create gson instances in 99,9% cases
return context.deserialize(jsonElement, MyObject.SingleObject.class);
}
if ( jsonElement.isJsonArray() ) {
return multiple(context.deserialize(jsonElement, genericListType));
}
// Or create a more sophisticated detector... Or redesign your mappigns
if ( jsonElement.isJsonPrimitive() ) {
throw new JsonParseException("Cannot parse primitives");
}
throw new AssertionError(jsonElement);
}
}
Example use:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(MyObject.class, getMyObjectJsonDeserializer())
.create();
public static void main(final String... args)
throws IOException {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43946453.class, "polymorphic.json") ) {
final JsonResponse response = gson.fromJson(jsonReader, JsonResponse.class);
for ( final MyObject datum : response.data ) {
System.out.println(datum.getClass().getSimpleName());
}
}
}
Output:
MultipleObjects
SingleObject