50

I'm a new Java programmer coming from a background in Python. I have weather data that's being collected/returned as a JSON with nested keys in it, and I don't understand how pull the values out in this situation. I'm sure this question has been asked before, but I swear I've Googled a great deal and I can't seem to find an answer. Right now I'm using json-simple, but I tried switching to Jackson and still couldn't figure out how to do this. Since Jackson/Gson seem to be the most used libraries, I'd would love to see an example using one of those libraries. Below is a sample of the data, followed by the code I've written so far.

{
    "response": {
        "features": {
            "history": 1
        }
     },
    "history": {
        "date": {
            "pretty": "April 13, 2010",
            "year": "2010",
            "mon": "04",
            "mday": "13",
            "hour": "12",
            "min": "00",
            "tzname": "America/Los_Angeles"
        },
        ...
    }
}

Main function

public class Tester {

    public static void main(String args[]) throws MalformedURLException, IOException, ParseException {
        WundergroundAPI wu =  new WundergroundAPI("*******60fedd095");

        JSONObject json = wu.historical("San_Francisco", "CA", "20100413");

        System.out.println(json.toString());
        System.out.println();
        //This only returns 1 level. Further .get() calls throw an exception
        System.out.println(json.get("history"));
    }
}

The function 'historical' calls another function that returns a JSONObject

public static JSONObject readJsonFromUrl(URL url) throws MalformedURLException, IOException, ParseException {

    InputStream inputStream = url.openStream();

    try {
        JSONParser parser = new JSONParser();
        BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));

        String jsonText = readAll(buffReader);
        JSONObject json = (JSONObject) parser.parse(jsonText);
        return json;
    } finally {
        inputStream.close();
    }
}
richsage
  • 26,912
  • 8
  • 58
  • 65
Jon.H
  • 794
  • 2
  • 9
  • 23
  • possible duplicate of [How to parse JSON in Java](http://stackoverflow.com/questions/2591098/how-to-parse-json-in-java) – Bobulous Apr 24 '15 at 22:19

4 Answers4

119

With Jackson's tree model (JsonNode), you have both "literal" accessor methods ('get'), which returns null for missing value, and "safe" accessors ('path'), which allow you to traverse "missing" nodes. So, for example:

JsonNode root = mapper.readTree(inputSource);
int h = root.path("response").path("history").getValueAsInt();

which would return the value at given path, or, if path is missing, 0 (default value)

But more conveniently, you can just use JSON pointer expression:

int h = root.at("/response/history").getValueAsInt();

There are other ways too, and often it is more convenient to actually model your structure as Plain Old Java Object (POJO). Your content could fit something like:

public class Wrapper {
  public Response response;
} 
public class Response {
  public Map<String,Integer> features; // or maybe Map<String,Object>
  public List<HistoryItem> history;
}
public class HistoryItem {
  public MyDate date; // or just Map<String,String>
  // ... and so forth
}

and if so, you would traverse resulting objects just like any Java objects.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • Thank you for your response. Could you possibly explain what the advantages of structuring the object as a POJO would be? To be honest, as a new Java programmer, the POJO example is completely unreadable to me. – Jon.H Apr 27 '15 at 19:23
  • 3
    Since Java is statically typed, unlike Python (or perhaps more-so), I think POJO model fits more naturally since you then work on basic Java objects, and not worry about JSON part. You just define objects to align with JSON, and library (Jackson, GSON) can convert from one to the other. If so, all construction, manipulation, changing of data happens with POJOs, and convertion to/from JSON is separate automated serialization step. Some developers find this simpler and more natural, whereas others prefer more dynamic tree access. So part of that goes to preferences, others to use cases. – StaxMan Apr 27 '15 at 19:32
  • 1
    ... I wish I had a simple example, but I think many REST services (JAX-RS, DropWizard) can show some of the usage advantages. Basically the whole existence of JSON and/or XML can be abstracted out; input and output is in form of objects. I think this is the main benefit really. But as I said, part of it is matter of taste, personal preferences. – StaxMan Apr 27 '15 at 19:33
  • 1
    Though the more I look at other implementations of Jackson, the more I think I'm beginning to understand the elegance behind modeling data like this as a POJO, for now the explanation of how to use the JsonNode object in Jackson is exactly what I need. Thank you! – Jon.H Apr 27 '15 at 22:10
  • @StaxMan I have a similar question on Jackon [here](https://stackoverflow.com/questions/48433294/design-pojo-for-nested-levels-of-a-field-and-extract-id-from-the-last-level) w.r.t nested level of a field. Wanted to see if you can help me out. – user1950349 Jan 25 '18 at 05:23
  • 1
    Is there a way to return null instead of value? 0 or an empty string are valid values in many cases and hard to distinguish from non-existent. – Ville Sep 16 '19 at 11:15
8

Use Jsonpath

Integer h = JsonPath.parse(json).read("$.response.repository.history", Integer.class);

Atmaram
  • 101
  • 1
  • 6
1

Check out Jackson's ObjectMapper. You can create a class to model your JSON then use ObjectMapper's readValue method to 'deserialize' your JSON String into an instance of your model class. And vice-versa.

Neil Davis
  • 31
  • 4
0

Try jpath API. It's xpath equivalent for JSON Data. You can read data by providing the jpath which will traverse the JSON data and return the requested value.

This Java class is the implementation as well as it has example codes on how to call the APIs.

https://github.com/satyapaul/jpath/blob/master/JSONDataReader.java

Readme -

https://github.com/satyapaul/jpath/blob/master/README.md

Example:

JSON Data:

{
    "data": [{
        "id": "13652355666_10154605514815667",
        "uid": "442637379090660",
        "userName": "fanffair",
        "userFullName": "fanffair",
        "userAction": "recommends",
        "pageid": "usatoday",
        "fanPageName": "USA TODAY",
        "description": "A missing Indonesian man was found inside a massive python on the island of Sulawesi, according to local authorities and news reports. ",
        "catid": "NewsAndMedia",
        "type": "link",
        "name": "Indonesian man swallowed whole by python",
        "picture": "https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBQf3loH5-XP6hH&w=130&h=130&url=https%3A%2F%2Fwww.gannett-cdn.com%2F-mm-%2F1bb682d12cfc4d1c1423ac6202f4a4e2205298e7%2Fc%3D0-5-1821-1034%26r%3Dx633%26c%3D1200x630%2Flocal%2F-%2Fmedia%2F2017%2F03%2F29%2FUSATODAY%2FUSATODAY%2F636263764866290525-Screen-Shot-2017-03-29-at-9.27.47-AM.jpg&cfs=1&_nc_hash=AQDssV84Gt83dH2A",
        "full_picture": "https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBQf3loH5-XP6hH&w=130&h=130&url=https%3A%2F%2Fwww.gannett-cdn.com%2F-mm-%2F1bb682d12cfc4d1c1423ac6202f4a4e2205298e7%2Fc%3D0-5-1821-1034%26r%3Dx633%26c%3D1200x630%2Flocal%2F-%2Fmedia%2F2017%2F03%2F29%2FUSATODAY%2FUSATODAY%2F636263764866290525-Screen-Shot-2017-03-29-at-9.27.47-AM.jpg&cfs=1&_nc_hash=AQDssV84Gt83dH2A",
        "message": "Akbar Salubiro was reported missing after he failed to return from harvesting palm oil.",
        "link": "http:\/\/www.usatoday.com\/story\/news\/nation-now\/2017\/03\/29\/missing-indonesian-man-swallowed-whole-reticulated-python\/99771300\/",
        "source": "",
        "likes": {
            "summary": {
                "total_count": "500"
            }
        },
        "comments": {
            "summary": {
                "total_count": "61"
            }
        },
        "shares": {
            "count": "4"
        }
    }]

}

Code snippet:

String jPath = "/data[Array][1]/likes[Object]/summary[Object]/total_count[String]";

String value = JSONDataReader.getStringValue(jPath, jsonData);
Satyajit Paul
  • 335
  • 2
  • 8