简体   繁体   中英

Jackson parse tree response

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 ?

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();
}

Since you will end up with a collection of NearbyPlace s, 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 NearbyPlace s.

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, ie geometry or location .

Please refer to Jackson annotations documentation for further details.

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