Tutorial: Using GSON to Fetch and Parse JSON into Java Models for Android
Update August 13, 2016:
I’ve written a new tutorial with a more modern approach to using GSON with Volley instead of AsyncTask that I recommend reading instead. To go to the new tutorial, please click here.
Original Post
This tutorial will cover how to fetch and parse JSON from a remote server on Android. We will use GSON, a JSON parsing library developed by Google, to quickly parse the JSON into Java objects with very minimal work required.
Before we begin, head over here to take a look at the JSON we will be working with. This JSON was designed using the Grails JSON object marshaller explained in a previous post.
Project Setup:
- Create a new Android project. You can target as low as Android 1.5 (or as high as you wish) with the code we will be using. Make sure you select Create an Activity in the project creation wizard, and name it PostsActivity
- Add the Internet permission to your AndroidManifest.xml
- Download the latest version of GSON, and add it to your project by copying the jar file to the project's libs directory. If you do not see the jar file in your libs directory within Eclipse, right click and select Refresh</i>
- Create two classes, Post and Tag. These will be our entity classes that model the data retrieved by the REST calls. (For the sake of brevity, I have made all of their properties public, rather than write out all of the accessor and mutator methods.) You will notice that some fields have the @SerializedName("") annotation. This denotes that the property name does not match the field name in our JSON. If both names do match, there is no need for the annotation. </ol>
<uses-permission android:name="android.permission.INTERNET"/>
//Post.java import java.util.Date; import java.util.List; import com.google.gson.annotations.SerializedName; public class Post { @SerializedName("id") public long ID; public String title; public String author; public String url; @SerializedName("date") public Date dateCreated; public String body; public List<Tag> tags; public Post() { } } //Tag.java public class Tag { public String name; public String url; public Tag() { } }
Now that the project has been setup, we are ready to start parsing some JSON. The first step is to write a simple asynchronous method that will fetch the JSON from the remote server without blocking the main thread. (This is very important for the same reasons discussed here.)
Let's add a private class to our activity called PostFetcher, which will asynchronously (you guessed it) fetch posts, and inform PostsActivity when posts have been loaded, or there has been an error. For now, our activity will simple show a Toast message for each post returned, or if there is an error.
package com.example.kylewbanksblog; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; 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.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import android.os.AsyncTask; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.Menu; import android.widget.Toast; public class PostsActivity extends Activity { private static final String TAG = "PostsActivity"; private List<Post> posts; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_posts); PostFetcher fetcher = new PostFetcher(); fetcher.execute(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.posts, menu); return true; } private void handlePostsList(List<Post> posts) { this.posts = posts; runOnUiThread(new Runnable() { @Override public void run() { for(Post post : PostsActivity.this.posts) { Toast.makeText(PostsActivity.this, post.title, Toast.LENGTH_SHORT).show(); } } }); } private void failedLoadingPosts() { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(PostsActivity.this, "Failed to load Posts. Have a look at LogCat.", Toast.LENGTH_SHORT).show(); } }); } private class PostFetcher extends AsyncTask<Void, Void, String> { private static final String TAG = "PostFetcher"; public static final String SERVER_URL = "http://kylewbanks.com/rest/posts.json"; @Override protected String doInBackground(Void... params) { try { //Create an HTTP client HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(SERVER_URL); //Perform the request and check the status code HttpResponse response = client.execute(post); StatusLine statusLine = response.getStatusLine(); if(statusLine.getStatusCode() == 200) { HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); try { //Read the server response and attempt to parse it as JSON Reader reader = new InputStreamReader(content); GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setDateFormat("M/d/yy hh:mm a"); Gson gson = gsonBuilder.create(); List<Post> posts = new ArrayList<Post>(); posts = Arrays.asList(gson.fromJson(reader, Post[].class)); content.close(); handlePostsList(posts); } catch (Exception ex) { Log.e(TAG, "Failed to parse JSON due to: " + ex); failedLoadingPosts(); } } else { Log.e(TAG, "Server responded with status code: " + statusLine.getStatusCode()); failedLoadingPosts(); } } catch(Exception ex) { Log.e(TAG, "Failed to send HTTP POST request due to: " + ex); failedLoadingPosts(); } return null; } } }
A few things to note from the PostFetcher class. The first is that we need to define how date's will be parsed if we want the Post class to get the proper date object. We could just have Post keep hold of a string, but a Date is much better as we can perform calculations on it, and display it in the app user's timezone. The second important thing to note is that we need to tell GSON what kind of class we are expecting to get back (in this case and array of posts).
GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setDateFormat("M/d/yy hh:mm a"); //Format of our JSON dates Gson gson = gsonBuilder.create(); List<Post> posts = new ArrayList<Post>(); //Instruct GSON to parse as a Post array (which we convert into a list) posts = Arrays.asList(gson.fromJson(reader, Post[].class));
Hopefully this gives you an idea of just how powerful GSON is, and how quickly you can get data flowing to your apps. Now that you have the Post and Tag instances loaded on your device, you can use them just like any other object. Show them in a ListView or link to their specific web pages using the post's url property.
Combine this quick JSON parsing with the efficient JSON creation of Grails, and you can start building fully functional application prototypes in as little as a few hours that actually communicate with each other.
Feel free to clone the full source on GitHub, and for more on Android development, check out my post on Creating a Square Camera.
Update - Implementing a Client Side SQLite Database for Cacheing
I've decided to go a step further and show how you can actually cache this data in a client-side SQLite database to reduce network traffic and improve load times in your app. Check out this post for more details!