8

I'm working with a REST API that returns a JSON document that starts as follows and includes a "collection" of items with string IDs like "ABC". Note the "routes" field, which contains a series of fields called "ABC", "ABD", "ABE" etc, however routes is not represented as an array in the json, so all these

{
"status":true,
"page":1,
"per_page":500,
"total_count":1234,
"total_pages":8,
"total_on_page":500,
"routes":{
    "ABC":[
    {
        "value":22313,
        <......>

I'm using Retrofit and the problem is the routes field is not an array (despite the fact conceptually it certainly is) and Retrofit/Gson require me to create a model object for routes with field vars abc, abd, etc - this is not practical as the data changes. For various reasons changing the server API is hard, so I'm looking to work around this on the Android client.

I figure these are options:

  • Intercept the JSON document before it reaches Gson and tweak the document, possibly with a customised Gson parser, or by intercepting the HTTP response.

  • Bypass the JSON parsing, and acquire the JSON document from Retrofit (I've yet to figure out how to do this, or if it's possible)

  • Use some feature of Retrofit I'm unaware of to map field names to a collection.

I'd appreciate help, especially if there's a quick and easy way to resolve this.

Ollie C
  • 28,313
  • 34
  • 134
  • 217

2 Answers2

14

It turns out that Retrofit's use of Gson by default makes it fairly easy to add a custom deserialiser to handle the portion of the JSON document that was the problem.

RestAdapter restAdapter = new RestAdapter.Builder()
        .setEndpoint(ApiDefinition.BASE_URL)
        .setConverter(getGsonConverter())
        .build();

public Converter getGsonConverter() {
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(RouteList.class, new RouteTypeAdapter())
            .create();
    return  new GsonConverter(gson);
}


public class RouteTypeAdapter implements JsonDeserializer<RouteList> {
    @Override
    public RouteList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Gson gson = new Gson();
        RouteList routeList = new RouteList();
        JsonObject jsonObject = json.getAsJsonObject();
        for (Map.Entry<String,JsonElement> elementJson : jsonObject.entrySet()){
            RouteList wardsRoutes = gson.fromJson(elementJson.getValue().getAsJsonArray(), RouteList.class);
            routeList.addAll(wardsRoutes);
        }
        return routeList;
    }

}
Ollie C
  • 28,313
  • 34
  • 134
  • 217
2

After calling RestService, don't use Model Name as argument, you have to use Default Response class from retrofit library.

RestService Method

@FormUrlEncoded
    @POST(GlobalVariables.LOGIN_URL)
    void Login(@Field("email") String key, @Field("password") String value, Callback<Response> callback);

Calling method in Activity

getService().Login(email, password, new MyCallback<Response>(context, true, null)
{
    @Override
    public void failure(RetrofitError arg0)
     {
        // TODO Auto-generated method stub
        UtilitySingleton.dismissDialog((BaseActivity<?>) context);
        System.out.println(arg0.getResponse());
      }

    @Override
    public void success(Response arg0, Response arg1)
    {
         String result = null;
         StringBuilder sb = null;
         InputStream is = null;
         try
         {
                is = arg1.getBody().in();
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                sb = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null)
                {
                    sb.append(line + "\n");
                    result = sb.toString();
                    System.out.println("Result :: " + result);
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    });
Manish Dubey
  • 4,206
  • 8
  • 36
  • 65