Retrofit: How To Parse A Json Array That Combines An Array And An Object?
Solution 1:
There is a way.
Add a custom JsonDeserializer
and handle that field manually.
Examples
publicstaticclassDeserializerimplementsJsonDeserializer<JsonResponse> {
privatefinalGsongson=newGsonBuilder().create();
@Overridepublic JsonResponse deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)throws JsonParseException {
JsonResponseresponse= gson.fromJson(json, typeOfT);
JsonObjectjObject= json.getAsJsonObject();
//Handle jObject here, and parse each object of data array //accordingly to its type - JsonObject/JsonArrayreturn response;
}
}
You need to register it in your global Gson instance, like this:
new GsonBuilder()
.registerTypeAdapter(JsonResponse.class, new JsonResponse.Deserializer())
.create();
Solution 2:
You seem to have not use List<MyObject>
because you have to create at least two kinds of objects: "single" and "multiple" where the latter must implement List<E>
to satisfy the array-like interface.
For example. JsonResponse
is simpler than what jsonschema2pojo generates:
finalclassJsonResponse {
finalbooleansuccess= Boolean.valueOf(false);
finalStringmessage=null;
final List<MyObject> data = null;
}
Now, MyObject
can look like this:
abstractclassMyObjectimplementsParcelable {
privateMyObject() {
}
static <E> MyObject multiple(final List<E> list) {
returnnewMultipleObjects<>(list);
}
staticfinalclassSingleObjectextendsMyObject {
privateSingleObject() {
}
finalStringinfo=null;
finalStringinfo2=null;
finalStringinfo3=null;
}
staticfinalclassMultipleObjects<E>
extendsMyObjectimplementsList<E> {
privatefinal List<E> list;
privateMultipleObjects(final List<E> list) {
this.list = list;
}
// @formatter:off@Overridepublicintsize() { return list.size(); }
@OverridepublicbooleanisEmpty() { return list.isEmpty(); }
@Overridepublicbooleancontains(final Object o) { return list.contains(o); }
@Overridepublic Iterator<E> iterator() { return list.iterator(); }
@Overridepublic Object[] toArray() { return list.toArray(); }
@Overridepublic <T> T[] toArray(final T[] a) { return list.toArray(a); }
@Overridepublicbooleanadd(final E e) { return list.add(e); }
@Overridepublicbooleanremove(final Object o) { return list.remove(o); }
@OverridepublicbooleancontainsAll(final Collection<?> c) { return list.containsAll(c); }
@OverridepublicbooleanaddAll(final Collection<? extends E> c) { return list.addAll(c); }
@OverridepublicbooleanaddAll(finalint index, final Collection<? extends E> c) { return list.addAll(index, c); }
@OverridepublicbooleanremoveAll(final Collection<?> c) { return list.removeAll(c); }
@OverridepublicbooleanretainAll(final Collection<?> c) { return list.retainAll(c); }
@Overridepublicvoidclear() { list.clear(); }
@Overridepublic E get(finalint index) { return list.get(index); }
@Overridepublic E set(finalint index, final E element) { return list.set(index, element); }
@Overridepublicvoidadd(finalint index, final E element) { list.add(index, element); }
@Overridepublic E remove(finalint index) { return list.remove(index); }
@OverridepublicintindexOf(final Object o) { return list.indexOf(o); }
@OverridepublicintlastIndexOf(final Object o) { return list.lastIndexOf(o); }
@Overridepublic ListIterator<E> listIterator() { return list.listIterator(); }
@Overridepublic ListIterator<E> listIterator(finalint index) { return list.listIterator(index); }
@Overridepublic List<E> subList(finalint fromIndex, finalint 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:
finalclassMyObjectJsonDeserializerimplementsJsonDeserializer<MyObject> {
privatestaticfinal JsonDeserializer<MyObject> myObjectJsonDeserializer = newMyObjectJsonDeserializer();
// You have to detect it more accurately yourself privatestaticfinalTypegenericListType=newTypeToken<List<Object>>() {
}.getType();
privateMyObjectJsonDeserializer() {
}
static JsonDeserializer<MyObject> getMyObjectJsonDeserializer() {
return myObjectJsonDeserializer;
}
@Overridepublic MyObject deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)throws JsonParseException {
if ( jsonElement.isJsonNull() ) {
returnnull;
}
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% casesreturn context.deserialize(jsonElement, MyObject.SingleObject.class);
}
if ( jsonElement.isJsonArray() ) {
return multiple(context.deserialize(jsonElement, genericListType));
}
// Or create a more sophisticated detector... Or redesign your mappignsif ( jsonElement.isJsonPrimitive() ) {
thrownewJsonParseException("Cannot parse primitives");
}
thrownewAssertionError(jsonElement);
}
}
Example use:
privatestaticfinalGsongson=newGsonBuilder()
.registerTypeAdapter(MyObject.class, getMyObjectJsonDeserializer())
.create();
publicstaticvoidmain(final String... args)throws IOException {
try ( finalJsonReaderjsonReader= getPackageResourceJsonReader(Q43946453.class, "polymorphic.json") ) {
finalJsonResponseresponse= gson.fromJson(jsonReader, JsonResponse.class);
for ( final MyObject datum : response.data ) {
System.out.println(datum.getClass().getSimpleName());
}
}
}
Output:
MultipleObjects SingleObject
Post a Comment for "Retrofit: How To Parse A Json Array That Combines An Array And An Object?"