Skip to content Skip to sidebar Skip to footer

Retrofit: How To Parse A Json Array That Combines An Array And An Object?

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, but so far

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?"