5

I want to parse google nearby places response, an item have this format :

         "geometry" : {
            "location" : {
               "lat" : 75.22404,
               "lng" : 57.42276
            },
            "viewport" : {
               "northeast" : {
                  "lat" : 95.2353532,
                  "lng" : 75.4427513
               },
               "southwest" : {
                  "lat" : 55.207256,
                  "lng" : 45.4045009
               }
            }
         },
         "vicinity" : "something"

But I want to parse this using only one object something like that :

public class NearbyPlace extends BaseResponse {

    @JsonProperty("how to access geometry->lat ?")
    private double latitude;

    @JsonProperty("how to access geometry->lng ?")
    private double longitude;

    @JsonProperty("vicinity")
    private String vicinity;
}

The problem is how to access "lat" and "lng" in "geometry" directly from NearbyPlace class without creating another classes for each node ?

GeniDeveloper
  • 361
  • 3
  • 18

3 Answers3

0

You can use a combination of readTree() and treeToValue():

final String placesResponse = "...";
final ObjectMapper om;

NearbyPlace place = null;
final JsonNode placesNode = om.readTree(placesResponse);
final JsonNode locationNode = placesNode.findPath("geometry").findPath("location");
if (! locationNode.isMissingNode()) {
     place = om.treeToValue(locationNode, NearbyPlace.class);
}

However, since vicinity kept outside of the inner geometry class, you still need to set that value manually. JsonNode has the necessary methods:

final JsonNode vicinityNode = placesNode.findPath("vicinity");
if (vicinityNode.isTextual()) {
    place.vicinity = vicinityNode.textValue();
}
dhke
  • 15,008
  • 2
  • 39
  • 56
  • 2
    Thanks for the answer, but I am using one ObjectMapper to map all class type : result = (T) objectMapper.readValue(resultString, aClass); So I need to solve my problem inside the mapped class it self, in this case : NearbyPlace – GeniDeveloper Jul 09 '15 at 09:10
0

Since you will end up with a collection of NearbyPlaces, you may be best off just manually traversing the JsonNode. Otherwise you're talking about overriding deserialization for collections, or writing a deserializer that is likely to get nasty.

The example below is recursive. Recursion in Java is bad (for now), but fun to write. In a production app I'd recommend a loop.

@Test
public void testNearbyPlaceDeserialization() throws Exception {
    JsonNode jsonNode = objectMapper.readTree(new File("input.json"));
    // or objectMapper.readValue(resultString, JsonNode.class);
    ImmutableList<NearbyPlace> nearbyPlaces = readLatLng(jsonNode, 
            jsonNode.get("vicinity").asText(null), 
            ImmutableList.builder());
    System.out.println(nearbyPlaces);
}

private static ImmutableList<NearbyPlace> readLatLng(JsonNode jsonNode, 
                                                     String vicinity, 
                                                     ImmutableList.Builder<NearbyPlace> placeBuilder) {
    JsonNode latNode = jsonNode.get("lat");
    JsonNode lngNode = jsonNode.get("lng");
    if (latNode != null && lngNode != null) {
        placeBuilder.add(NearbyPlace.builder()
                .setLatitude(latNode.asDouble())
                .setLongitude(lngNode.asDouble())
                .setVicinity(vicinity)
                .build());
    } else {
        jsonNode.elements().forEachRemaining((element) -> {
            readLatLng(element, vicinity, placeBuilder);
        });
    }
    return placeBuilder.build();
}

This will return a list of 3 NearbyPlaces.

Community
  • 1
  • 1
Sam Berry
  • 7,394
  • 6
  • 40
  • 58
0

The easiest solution I can think of is to use the @JsonCreator annotation on your NearbyPlace class' constructor:

public class NearbyPlace extends BaseResponse {

    private double latitude;

    private double longitude;

    @JsonProperty("vicinity")
    private String vicinity;

    @JsonCreator
    public NearbyPlace(Map<String, Object> delegate) {
        super();
        this.latitude = (Double) delegate.get("geometry").get("location").get("lat");
        this.latitude = (Double) delegate.get("geometry").get("location").get("lng");
    }
}

You might want to add some checks against null in case the incoming JSON lacks some nested object, i.e. geometry or location.

Please refer to Jackson annotations documentation for further details.

fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    Thank you for answering, it looks helpful but I can't test it today. I will try to test it tomorrow and give feedback. – GeniDeveloper Jul 11 '15 at 06:56
  • I checked and now I have latitude and longitude have value but all other fields that uses annotations are null, do you have idea ? – GeniDeveloper Jul 12 '15 at 07:12
  • Well. .. havent tested. Maybe you have to extract them from the delegate map in the constructor and assign each value to its corresponding attribute. – fps Jul 12 '15 at 11:29