简体   繁体   中英

Using Retrofit 2 For getting and fetching Unknown set of data

Retrofit is a wonderful library, it is simple to use and maintain, but My case is different, as we know, we should apply the stages of creating a retrofit api and response accordingly,

retrofit instance:

retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(new ErrorCallAdapter.MyCallAdapterFactory())
                .client(httpClient)
                .build();
        return retrofit;

API Interface:

 @GET("api/getTheResults")
 ErrorCallAdapter.MyCall<GeneralResponse<SomeEntity> getGenericMethod();

part of the ErrorCallAdapter:

@Override
        public void enqueue(final MyCallback<Object> callback) {
            call.enqueue(new Callback<GeneralResponse<T>>() {
                @Override
                public void onResponse(final Call<GeneralResponse<T>> call, final Response<GeneralResponse<T>> response) {
                    final int code = response.code();
                    mainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (response.isSuccessful()) {
                                if (response.body().getStatus() == 1) {
                                    callback.onResult(response.body().getResponse());
                                } else
                                    callback.onError(response.body().getMError());
                            } else
                                callback.onError(new MError(code,
                                        response.message() != null ? response.message() : "onResponse error"));
                        }
                    });
                }

                @Override
                public void onFailure(Call<GeneralResponse<T>> call, Throwable t) {
                    if (t != null)
                        callback.onError(new MError(501,
                                t.getMessage() != null ? t.getMessage() : "unexpected error"));
                    else
                        callback.onError(new MError(501,
                                "unexpected error"));
                }
            });

        }

part of fetching Data:

public void loadUserSetupData() {
        ErrorCallAdapter.MyCall<GeneralResponse<Data>, Data> DataCall = apiManager.getData();
        setupDataMyCall.enqueue(new ErrorHandlingCallAdapter.MyCallback<Data>() {
            @Override
            public void onResult(Data data) {
                endProgressDialog();

                apiResult.onSuccess(data);
            }

            @Override
            public void onError(MError mError) {
                endProgressDialog();
                apiResult.onError(mError.getErrorMessage(mContext));
            }
        });
    }

The data I am Trying to fetch is in JSON Format as:

{
    "Status": 1,
    "Error": null,
    "ResponseList": [
        {
            "ErCode": 0,
            "Masseg": null,
            "DType": "OnceData",
            "Contents": {
                "VList": [{
                    "name":"name"
                    "name":"name"
                    "name":"name"
                  }],
                "WList": [{
                    "name":"name"
                    "name":"name"
                    "name":"name"
                  }, {
                    "name":"name"
                    "name":"name"
                    "name":"name"
                  }],
                "UList": [{
                    "name":"name"
                    "dd":"ddd" "DobledataType"
                    "object":"name"  "object data type"
                  }, {
                    "name":"name"
                    "int":"1"
                    "code":"15.00" "float data type
                  }],

            }
        }
    ]
}

"Above I refer to "name" as an entity object ( each of UList, VList, WList are objects for illustration only).

My Question is : how can I make the retrofit works for any type of data where it will fetch the object whatever type it is, means I don't know if the "Contents" list will have specific type of object, I want to make it work where I can receive any type whatever it types is without knowing the object, we can specify the msg, the DType ( know in previous that DType = data for example will do some action) but we don't know what is the type of the response object (arraylist of strings, objects, integers , or just one string) How are we do that in retrofit Android?

If you can never know at all what is the content of the "Contents" field is, you can maybe set it directly as a JSONObject in your model, but then you will have to do some work everytime you want to retrieve something from the contents field.

However, if you know what type it can have, for example you know for sure it will be either an array of String, a String or an int, then you have the solution of using a custom GSON deserializer .

First you would have to change your Model/Pojo to handle every types, for instance:

abstract class Model {
    // put here all the fields you know the type is stable
}

// for when content is a list
public class ListModel extends Model {
    @SerializedName("Contents")
    List<String> content;
}

// for when content is a String
public class StringModel extends Model {
    @SerializedName("Contents")
    String content;
}

// add every other derived class you need for the other types that the content field can have

Now you can create a deserializer that will return the correct Model object:

public class ModelDeserializer implements JsonDeserializer<Model> {
    @Override
    public Model deserialize(JsonElement json, Type typeOfT, 
                             JsonDeserializationContext context) 
                             throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();

        // get the "contents" field
        JsonElement contents = jsonObject.get("Contents");
        if (contents != null) {
            if (contents.isJsonArray()) { // if this is a list, parse as ListModel
                return context.deserialize(jsonObject, 
                    ListModel.class);
            }
            if (contents.isJsonPrimitive()) { // json primitives are number, string, bool, character
                if (contents.getAsJsonPrimitive().isString() {
                    return context.deserialize(jsonObject, StringModel.class);
                }
            }
            // do the same for every other type you might have
        }
        return null;
    }
}

and finally, you will have to tell Retrofit and Gson to use that deserializer:

Gson gson = new GsonBuilder()
         .registerTypeAdapter(Model.class, new ModelDeserializer())
         .create();

// just add the new gson object to your converterfactory
retrofit = new Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(new ErrorCallAdapter.MyCallAdapterFactory())
            .client(httpClient)
            .build();
    return retrofit;

and it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM