Network Resources - JSON

Intro

In addition to XML/RSS data, much information on the internet is made available using the JSON format.  Short for "JavaScript Object Notation", JSON formats data into Objects and Arrays using attribute:value pairs.  Like XML, JSON objects and arrays are nestable.  Most data that is formatted in XML can also be represented using JSON, and vice-versa.  This lesson is not intended to teach you the details of JSON (you can get that info by following the link in the first sentence), but rather, how to parse and display JSON-formatted data using the Android SDK.  As with XML, there are groups that advocate for/against it.  We'll remain non-partisan in that discussion, and instead focus on how to use it.

Retrieving JSON Data

Retrieving data that has been formatted in JSON requires the use of an HttpURLConnection object.  HttpURLConnection is a powerful class that allows you to do much more than what's demonstrated in this lesson, such as cookie management, website authentication, etc.  I encourage you to read through the developer documents when you get a spare minute.  

To use HttpURLConnection, we create an URL object and call openConnection() on it.  We then evaluate the response in the form of an InputStream:

public String retrieveSiteContent(String siteUrl){
    StringBuilder builder = new StringBuilder();

    try {
        URL url = new URL(siteUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        InputStream content = new BufferedInputStream(urlConnection.getInputStream());
        BufferedReader reader = new BufferedReader(new InputStreamReader(content));
        String line;
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
        urlConnection.disconnect();
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return builder.toString();
}

Demonstration

For this lesson, we will use the API provided by the website Forecast.io to build an app that displays data about the current weather.  The API has a daily limit of 1000 calls for free.  If you plan to use the API for something other than this demo, you should sign up for your own API key (it's free) and use it as the apiKey variable value.

Run the app and bask in the glory of raw weather data at your fingertips!

Parsing JSON

Now that you know how to retrieve the JSON data, let's do something with it!  First, let's examine the format of the JSON we retrieved:

{
   "latitude":38.9784,
   "longitude":-76.4922,
   "timezone":"America/New_York",
   "offset":-4,
   "currently":{
      "time":1395872882,
      "summary":"Clear",
      "icon":"clear-day",
      "precipIntensity":0,
      "precipProbability":0,
      "temperature":34.91,
      "apparentTemperature":25.75,
      "dewPoint":12.38,
      "humidity":0.39,
      "windSpeed":13.77,
      "windBearing":295,
      "visibility":10,
      "cloudCover":0.01,
      "pressure":1021.51,
      "ozone":383.69
   },
   "minutely":{
      "summary":"Clear for the hour.",
      "icon":"clear-day",
      "data":[]
   },
   "hourly":{
      "summary":"Partly cloudy until tomorrow evening.",
      "icon":"partly-cloudy-day",
      "data":[]
   },
   "daily":{
      "summary":"Rain on Friday through Sunday, with temperatures rising to 61°F on Wednesday.",
      "icon":"rain",
      "data":[]
   },
   "flags":{}
}

The JSON in our feed is formatted as such:

In order to read the JSON data in our app, we'll use the library provided to us by Android.  The JSON library provided to us defines two JSON types:

Since our main element is an Object, we'll start with JSONObject.  The first thing we need to do is create a new JSONObject from our string: 

JSONObject jsonObj = new JSONObject(feedString);

To get a String value from the object, we call the getString(key) method:

String timeZone = jsonObj.getString("timezone");

Modify your app by placing the above code in the onPostExecute() method of the AsyncTask, and displaying the value of timeZone in the TextView.  You should see this:

It works...but it's pretty boring.  Let's fix that.

More Better Weather

1. Let's add a couple TextViews to show more info.  Modify your layout file to this:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_weight="0.3"
        android:textSize="96sp"
        android:gravity="center"
        android:text="76" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp" />
    
    <TextView
        android:id="@+id/textView3"
        android:paddingTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"/>

</LinearLayout>

2.  Modify you main activity's onCreate() to instantiate the new TextViews:

tv1 = (TextView) findViewById(R.id.textView1);
tv2 = (TextView) findViewById(R.id.textView2);
tv3 = (TextView) findViewById(R.id.textView3);

 3.  Modify the onPostExecute method:

@Override
protected void onPostExecute(String result){
	try {
		JSONObject jsonObj = new JSONObject(result);

		JSONObject currentlyObj = jsonObj.getJSONObject("currently");
		String currentlyTemp = currentlyObj.getString("temperature");
				
		JSONObject minutelyObj = jsonObj.getJSONObject("minutely");
		String minutelySummary = minutelyObj.getString("summary");
				
		JSONObject dailyObj = jsonObj.getJSONObject("daily");
		String dailySummary = dailyObj.getString("summary");
				
		tv1.setText(currentlyTemp + (char) 0x00B0);
		tv2.setText(minutelySummary);
		tv3.setText(dailySummary);
				
	} catch (JSONException e) {
		e.printStackTrace();
	}
			
}

That's a little better:

 

Play around with the layout to get it looking how you like.  Go to forecast.io and put in your home town.  In the resulting URL, copy the Lat,Long value.  Replace the value of the location String in the app with the value you just copied and run the app again.