Consuming JSON services in Android apps

Unless you are writing a Hello World Android application, chances are your application would need to connect to the outside world to fetch some data, such as live currency exchange rates, weather information, records from databases, etc. One of the easiest ways for your application to connect to the outside world is to use web services.

For the past few years, XML web services have dominated the arena for web services, as XML was touted as the ubiquitous medium for data exchange. However, using XML as the medium for your data payload suffers from the following problems:

1. XML representation is inherently heavy. The use of opening and closing tags add a lot of unnecessary weight to the payload. In the world of mobile applications, shaving a few bytes off the payload will dramatically improve the performance of applications, not to mention the reduction of data transferred over the expensive 3G and LTE wireless networks. This translates into cost savings for both application developers (who need to subscribe to expensive networks for their web servers) and users (who has limited amount of bandwidth to use per subscription).
2. XML representation is difficult to parse. While on the desktop, the DOM (Document Object Model) and SAX (Simple APIs for XML) are the two commonly used method for parsing XML Documents; on the mobile platform using DOM and SAX are very expensive, both computationally and in terms of memory requirements.

In recent years, another data interchange format has been gaining in popularity – JSON, or JavaScript Object Notation. Like XML, JSON is a text-based open standard for representing data, and it uses characters such as brackets “[{]}”, colon “:” and comma “,”, to represent data. Data are represented using simple key/value pairs, and more complex data are represented as associative arrays.

In this article, I will walk you through on how to consume a JSON service in your Android application.

Creating the Project

For this project, I will be using Eclipse with the Android 4.1 SDK. To start off, create an Android application project and name it as shown in Figure 1.

Figure 1: Create Android application project

In the res/layout folder, add in the following statements to the activity_main.xml file:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_alignParentLeft="true"

android:layout_alignParentRight="true"

android:layout_alignParentTop="true"

android:orientation="vertical">

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Latitude"/>

<EditText

android:id="@+id/txtLat"

android:layout_width="320dp"

android:layout_height="wrap_content"

android:ems="10"

android:inputType="numberDecimal"

android:text="37.77493"/>

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Longitude"/>

<EditText

android:id="@+id/txtLong"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:ems="10"

android:inputType="numberDecimal"

android:text="-122.419416"/>

<Button

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Get Weather"

android:onClick="btnGetWeather"/>

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Postal Code"/>

<EditText

android:id="@+id/txtPostalCode"

android:layout_width="320dp"

android:layout_height="wrap_content"

android:ems="10"

android:inputType="number"

android:text="89118"/>

<Button

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Get Places"

android:onClick="btnGetPlaces"/>

</LinearLayout>

This will create the UI as shown in Figure 2. As you can see, there are actually two parts to the UI:

1. The first part allows the user to enter a pair of latitude and longitude information. Click on the Get Weather button and you will be able to get information about the weather information for that particular location.
2. The second part allows the user to enter a postal code. Clicking the Get Places button will search for all the towns and cities in the world with this postal code.

In both cases, the data would be retrieved from web services hosted by GeoNames Web Services (http://www.geonames.org/export/web-services.html).

Figure 2: Create the UI

Creating the Helper Method

To connect to a web service, your application needs to first of all connect to the server using HTTP. You need to also determine if you will be using HTTP GET or HTTP POST. Once that is determined, you will fetch the data from the server and get ready for the next step, which is parsing. For this article, the GeoNames Web Services that you will be using uses HTTP GET, and hence, you will first of all create the helper method readJSONFeed() in the MainActivity.java file:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

packagenet.learn2develop.json;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.StatusLine;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

publicclassMainActivityextendsActivity{

publicStringreadJSONFeed(StringURL){

StringBuilder stringBuilder=newStringBuilder();

HttpClient httpClient=newDefaultHttpClient();

HttpGet httpGet=newHttpGet(URL);

try{

HttpResponse response=httpClient.execute(httpGet);

StatusLine statusLine=response.getStatusLine();

intstatusCode=statusLine.getStatusCode();

if(statusCode==200){

HttpEntity entity=response.getEntity();

InputStream inputStream=entity.getContent();

BufferedReader reader=newBufferedReader(

newInputStreamReader(inputStream));

Stringline;

while((line=reader.readLine())!=null){

stringBuilder.append(line);

}

inputStream.close();

}else{

Log.d("JSON","Failed to download file");

}

}catch(Exceptione){

Log.d("readJSONFeed",e.getLocalizedMessage());

}

returnstringBuilder.toString();

}

@Override

publicvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

The readJSONFeed() method takes in a string representing the URL of the web service and then connects to the server using HTTP GET. You make use of the HttpClient class to connect to the server, the HttpGet class to specify the URL of the server, and the HttpResponse class to get the connection status from the server. Once the connection is established successfully, you all use the BufferedReader and InputStreamReader classes to download the result (which is a JSON string in this example) from the server. The readJSONFeed() method then returns the JSON string.

As you need Internet access for this project to work, remember to add the INTERNET permission in the AndroidManifest.xml file:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="net.learn2develop.json"

android:versionCode="1"

android:versionName="1.0">

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="15"/>

<uses-permission android:name="android.permission.INTERNET"/>

<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

<activity

android:name=".MainActivity"

android:label="@string/title_activity_main">

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

</application>

</manifest>

Getting Weather Information

Now that you can connect to the server to download the JSON result, it is now time to connect to the GeoNames Web Services to get weather information. In particular, to get the weather information of a particular location, you will use the following URL (replace the and with the actual latitude and longitude):

http://ws.geonames.org/findNearByWeatherJSON?lat=&lng=

A sample result from the server looks like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

{

"weatherObservation":{

"clouds":"scattered clouds",

"weatherCondition":"n/a",

"observation":"KCFV 090852Z AUTO 06005KT

10SM SCT090 SCT110 24/20 A3000 RMK AO2

SLP148 T02390200 53002",

"windDirection":60,

"ICAO":"KCFV",

"seaLevelPressure":1014.8,

"elevation":225,

"countryCode":"US",

"lng":-95.56666666666666,

"temperature":"23.9",

"dewPoint":"20",

"windSpeed":"05",

"humidity":78,

"stationName":"Coffeyville, Coffeyville

Municipal Airport",

"datetime":"2012-07-09 08:52:00",

"lat":37.083333333333336

}

}

If you observe, you have a primary key – weatherObservation. Its value is a collection of key/value pairs, such as clouds, weatherCondition, etc.

In order to use the readJSONFeed() method, you need to call it asynchronously as beginning with Android 3.0 you can no longer call network operations from within your UI thread (such as within an activity). The easiest way to do this is to wrap it using an AsyncTask class. Hence, add the following statements to the MainActivity class:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

packagenet.learn2develop.json;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.StatusLine;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.json.JSONObject;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.widget.Toast;

publicclassMainActivityextendsActivity{

publicStringreadJSONFeed(StringURL){

StringBuilder stringBuilder=newStringBuilder();

HttpClient httpClient=newDefaultHttpClient();

HttpGet httpGet=newHttpGet(URL);

try{

HttpResponse response=httpClient.execute(httpGet);

StatusLine statusLine=response.getStatusLine();

intstatusCode=statusLine.getStatusCode();

if(statusCode==200){

HttpEntity entity=response.getEntity();

InputStream inputStream=entity.getContent();

BufferedReader reader=newBufferedReader(

newInputStreamReader(inputStream));

Stringline;

while((line=reader.readLine())!=null){

stringBuilder.append(line);

}

inputStream.close();

}else{

Log.d("JSON","Failed to download file");

}

}catch(Exceptione){

Log.d("readJSONFeed",e.getLocalizedMessage());

}

returnstringBuilder.toString();

}

privateclassReadWeatherJSONFeedTask extendsAsyncTask

<String,Void,String>{

protectedStringdoInBackground(String...urls){

returnreadJSONFeed(urls[0]);

}

protectedvoidonPostExecute(Stringresult){

try{

JSONObject jsonObject=newJSONObject(result);

JSONObject weatherObservationItems=

newJSONObject(jsonObject.getString("weatherObservation"));

Toast.makeText(getBaseContext(),

weatherObservationItems.getString("clouds")+

" - "+weatherObservationItems.getString("stationName"),

Toast.LENGTH_SHORT).show();

}catch(Exceptione){

Log.d("ReadWeatherJSONFeedTask",e.getLocalizedMessage());

}

}

}

@Override

publicvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

In the ReadWeatherJSONFeedTask class, you have two methods:

1. The doInBackGround() method is executed asynchronously. Here, you call the readJSONFeed() method to get the weather information. When the result is obtained, it is returned to the onPostExecute() method.
2. The onPostExecute() method takes the JSON result and parses it.
a. First, the JSON string is passed as the argument to the constructor of the JSONObject class. This creates a new JSONObject object (jsonObject) with key/value mappings from the JSON string.
b. You then get the value of the weatherObservation key by using the getString() method of jsonObject. The values are then passed as the constructor of the JSONObject class, creating another JSONObject – weatherObservationItems.
c. Finally, you extract the value of the clouds and stationName keys by calling the getString() method of weatherObservationItems.

To wire up the event handler for the Get Weather button in your UI, add the btnGetWeather() method to MainActivity.java:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

packagenet.learn2develop.json;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.StatusLine;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.json.JSONObject;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.EditText;

import android.widget.Toast;

publicclassMainActivityextendsActivity{

publicStringreadJSONFeed(StringURL){

...

}

privateclassReadWeatherJSONFeedTask extendsAsyncTask

<String,Void,String>{

...

}

@Override

publicvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

publicvoidbtnGetWeather(View view){

EditText txtLat=(EditText)findViewById(R.id.txtLat);

EditText txtLong=(EditText)findViewById(R.id.txtLong);

newReadWeatherJSONFeedTask().execute(

"http://ws.geonames.org/findNearByWeatherJSON?lat="+

txtLat.getEditableText().toString()+"&lng="+

txtLong.getText().toString());

}

}

You can now test the application on an Android emulator. Figure 3 shows the result.

Figure 3: Test in Emulator

Getting Places using Postal Code

Now that you have managed to consume the first JSON service, let’s take a look at the second example. This time, you will consume a service that returns a list of city names using a given postal code. You can get the result from the following URL (replace the with the actual postal code):

If you observe, you have a primary key – postalCodes. Its value is an array of objects, with each object containing a collection of key/value pairs, such as adminCode1, lat, lng, etc.

Like the previous example, you will add a ReadPlacesFeedTask class to connect to the server asynchronously:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

packagenet.learn2develop.json;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.StatusLine;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.json.JSONArray;

import org.json.JSONObject;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.EditText;

import android.widget.Toast;

publicclassMainActivityextendsActivity{

publicStringreadJSONFeed(StringURL){

...

}

privateclassReadWeatherJSONFeedTask extendsAsyncTask

<String,Void,String>{

...

}

privateclassReadPlacesFeedTask extendsAsyncTask

<String,Void,String>{

protectedStringdoInBackground(String...urls){

returnreadJSONFeed(urls[0]);

}

protectedvoidonPostExecute(Stringresult){

try{

JSONObject jsonObject=newJSONObject(result);

JSONArray postalCodesItems=new

JSONArray(jsonObject.getString("postalCodes"));

//---print out the content of the json feed---

for(inti=0;i<postalCodesItems.length();i++){

JSONObject postalCodesItem=

postalCodesItems.getJSONObject(i);

Toast.makeText(getBaseContext(),

postalCodesItem.getString("postalCode")+" - "+

postalCodesItem.getString("placeName")+", "+

postalCodesItem.getString("countryCode"),

Toast.LENGTH_SHORT).show();

}

}catch(Exceptione){

Log.d("ReadPlacesFeedTask",e.getLocalizedMessage());

}

}

}

@Override

publicvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

publicvoidbtnGetWeather(View view){

...

}

}

As in the previous example, the result is first converted into a JSONObject. The values of the postalCode key is then converted into a JSONArray object – postalCodesItems, which contains an array of JSONObject objects. You then iterate through the array and extracted each object and print out the values of the postalCode, placeName, and countryCode keys.

Finally, wire the event handler for the Get Places button with the btnGetPlaces() method in the MainActivity.java file:

1

2

3

4

5

6

7

8

9

10

11

publicvoidbtnGetWeather(View view){

...

}

publicvoidbtnGetPlaces(View view){

EditText txtPostalCode=(EditText)findViewById(R.id.txtPostalCode);

newReadPlacesFeedTask().execute(

"http://api.geonames.org/postalCodeSearchJSON?postalcode="+

txtPostalCode.getEditableText().toString()+

"&maxRows=10&username=demo");

}

Figure 4 shows the sample result.

Figure 4: Voilà – the result

Summary

In this article, you have seen how to consume a JSON service from within your Android application. You have seen two examples on how to parse a JSON string using the JSONObject and JSONArray classes available in the org.json package.

This is a website of Afilias Technologies Ltd, a private company limited by shares, incorporated and registered in the Republic of Ireland with registered number 398040 and registered office at 6th Floor, 2 Grand Canal Square, Dublin 2, Ireland