Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson - ignore json fields when parsing JSON to Object

There is a question here that is similar to my question but not exactly what I'm looking for.

I've a JSON response from a webservice, let's say this JSON response:

{
   "routes" : [
      {
         "bounds" : {
            "northeast" : {
               "lat" : 45.5017123,
               "lng" : -73.5672184
            },
            "southwest" : {
               "lat" : 43.6533103,
               "lng" : -79.3827675
            }
         },
         "copyrights" : "Dados do mapa ©2015 Google",
         "legs" : [
            {
               "distance" : {
                  "text" : "541 km",
                  "value" : 540536
               },
               "duration" : {
                  "text" : "5 horas 18 min.",
                  "value" : 19058
               },
               "end_address" : "Montreal, QC, Canada",
               "end_location" : {
                  "lat" : 45.5017123,
                  "lng" : -73.5672184
               },
               "start_address" : "Toronto, ON, Canada",
               "start_location" : {
                  "lat" : 43.6533103,
                  "lng" : -79.3827675
               }, 
               (...)

In this JSON I' just interested on the distance object. My question is, how can I ignore all the other fields?

I tried to build my object starting from legs, as it is the first non repetitive object name from distance to the root.

Here's my object:

public class MyObject {

    public ArrayList<Distance> legs;

    public static class Distance {
        public String text;
        public String value;
    }
}

but the ArrayList legs is always null.

How can I accomplish this? Ignore fields to the left from the pretended json field.

like image 315
dazito Avatar asked Feb 10 '15 02:02

dazito


1 Answers

I think that the philosophy of Gson is to map the Json structure to an objects graph. So in your case I would probably create all the needed java objects to map correctly the json structure. In addition of that, maybe one day you will need some other information of the response, so it will be easier to do the evolution. Something like that (the proper way I think):

class RouteResponse {
    private List<Route> routes;
}
class Route {
    private List<Bound> bounds;
    private String copyrights;
    private List<Leg> legs;
}
class Leg {
    private Distance distance;
    private Duration duration;
    private String endAddress;
    ...
}
class TextValue {
    private String text;
    private String value;
}
class Distance extends TextValue {
}
// And so on

And I would use an ExclusionStrategy to have light objects and to have only the fields I'm interested in. It sounds to me like the proper way to do that.

Now if you really want to retrieve only the list of distances, I'm sure you can do that with a custom TypeAdapter and TypeAdapterFactory.

Something like that (the bad way :-)):

The objects to map the response:

public class RouteResponse {

    private List<Distance> distances;

   // add getters / setters
}

public class Distance {

    private String text;
    private String value;

   // add getters / setters
}

The factory that instantiates our adapter (With a reference to the Gson object, so the adapter can retrieve delegates) :

public class RouteResponseTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (type.getRawType() == RouteResponse.class) {
            return (TypeAdapter<T>)new RouteResponseTypeAdapter(gson);
        }
        return null;
    }
}

And the type adapter: this implementation will first unmarshall the Json document to a JsonElements tree and then will retrieve the needed JsonObjects to create through the delegate the Distance objects (sorry for the bad code, quickly written).

public class RouteResponseTypeAdapter extends TypeAdapter<RouteResponse> {

    private final TypeAdapter<JsonElement> jsonElementTypeAdapter;
    private final TypeAdapter<Distance> distanceTypeAdapter;

    public RouteResponseTypeAdapter(Gson gson) {
        this.jsonElementTypeAdapter = gson.getAdapter(JsonElement.class);
        this.distanceTypeAdapter = gson.getAdapter(Distance.class);
    }

    @Override
    public void write(JsonWriter out, RouteResponse value) throws IOException {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public RouteResponse read(JsonReader jsonReader) throws IOException {
        RouteResponse result = new RouteResponse();
        List<Distance> distances = new ArrayList<>();
        result.setDistances(distances);
        if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
            JsonObject responseObject = (JsonObject) jsonElementTypeAdapter.read(jsonReader);
            JsonArray routes = responseObject.getAsJsonArray("routes");
            if (routes != null) {
                for (JsonElement element:routes) {
                    JsonObject route = element.getAsJsonObject();
                    JsonArray legs = route.getAsJsonArray("legs");
                    if (legs != null) {
                        for (JsonElement legElement:legs) {
                            JsonObject leg = legElement.getAsJsonObject();
                            JsonElement distanceElement = leg.get("distance");
                            if (distanceElement != null) {
                                distances.add(distanceTypeAdapter.fromJsonTree(distanceElement));
                            }
                        }
                    }
                }
            }
        }
        return result;
    }
}

Finally, you can parse your json document:

    String json = "{ routes: [ ....."; // Json document
    Gson gson = new GsonBuilder().registerTypeAdapterFactory(new RouteResponseTypeAdapterFactory()).create();
    RouteResponse response = gson.fromJson(json, RouteResponse.class);
    //response.getDistances() should contain the distances

Hope it helps.

like image 66
stef.1 Avatar answered Sep 20 '22 02:09

stef.1