Tutorial: Parsing JSON on Android using GSON and Volley
Introduction
A while back I wrote about how to Use GSON to Fetch and Parse JSON into Java Models for Android, and that post proved to be quite popular even to this day. However, it contains some out-of-date content and uses AsyncTask to perform the network requests off the main UI thread.
However, times have changed and we now have much better means of doing this, such as Volley. I’d like to provide you with a new tutorial that goes over how to perform the same task of fetching and parsing JSON into Java objects with GSON, but using updated libraries such as Volley, and demonstrate how much cleaner and more maintainable the code becomes.
Setup
We’ll be using the same API as the previous post, which returns the latest posts for this blog. Before we begin, take a look at the JSON structure we’ll be working with.
- Feel free to create a new Android project if you plan to follow along exactly. If not, you can adapt the content of this post to suit your needs in your own application.
- The first thing we’ll need to do is ensure we have the INTERNET permission as we’ll be fetching the JSON from a remote location. In your AndroidManifest.xml, add the following:
<uses-permission android:name="android.permission.INTERNET" />
- In your build.gradle you’ll need to add the dependencies for Volley and GSON:
dependencies { ... compile 'com.google.code.gson:gson:2.4' compile 'com.android.volley:volley:1.0.0' }
Define the Model
Now we’re ready to start writing some code. The first thing we’ll want to do is define our Post model.
Create a new file called Post.java and define the Post class like so:
import java.util.Date; import com.google.gson.annotations.SerializedName; public class Post { @SerializedName("id") long ID; @SerializedName("date") Date dateCreated; String title; String author; String url; String body; }
You’ll note the use of the SerializedName annotation on a couple of the fields. This is required only when the property in the JSON does not exactly match the field of your class. For instance, in the JSON the ID of each post is id, but in the Java code we want to use ID. In these cases, we annotate the field using @SerializedName(“JSON PROPERTY”) where the JSON PROPERTY is the key from the JSON.
Making the Request
Now that our Java class is defined, we’re ready to perform the network requests to load the posts. Let’s assume we have an Activity called PostActivity where we want to fetch and display the posts.
The first thing we’ll need to do is instantiate a RequestQueue which will handle running our request in a background thread:
public class PostActivity extends Activity { private static final String ENDPOINT = "https://kylewbanks.com/rest/posts.json"; private RequestQueue requestQueue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_post); requestQueue = Volley.newRequestQueue(this); } }
With our RequestQueue initialized, we’re ready to write the actual request code. We’ll be using the StringRequest class to retrieve the JSON as a String which can then be parsed and deserialized into instances of our Post class. Let’s create a new method called fetchPosts in the PostActivity, and call it immediately after initializing the RequestQueue:
public class PostActivity extends Activity { ... @Override protected void onCreate(Bundle savedInstanceState) { ... requestQueue = Volley.newRequestQueue(getApplicationContext()); fetchPosts(); } private void fetchPosts() { StringRequest request = new StringRequest(Request.Method.GET, ENDPOINT, onPostsLoaded, onPostsError); } private final Response.Listener<String> onPostsLoaded = new Response.Listener<String>() { @Override public void onResponse(String response) { Log.i("PostActivity", response); } }; private final Response.ErrorListener onPostsError = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("PostActivity", error.toString()); } }; }
In fetchPosts we instantiate a StringRequest that will perform a GET (the other HTTP actions are available as well) request to the static ENDPOINT we defined above, and provide it with two listeners: onPostsLoaded and onPostsError.
When we eventually perform the request, one of the two listeners will be executed depending on what occurs. Assuming everything goes well and the remote JSON is loaded, onPostsLoaded.onResponse will be called with the response in String format. If, for whatever reason, the network request fails or the server returns an error response, onPostsError.onErrorResponse will be executed with the error that occurred. For now the two listeners will just perform some logging, but we’ll be coming back to them in a minute.
With our request defined and our listeners in place, we’re ready to send the request and check the response by adding it to the RequestQueue:
private void fetchPosts() { StringRequest request = new StringRequest(Request.Method.GET, ENDPOINT, onPostsLoaded, onPostsError); requestQueue.add(request); }
And that’s it. Compared to the previous post using AsyncTask, this is a massive improvement in terms of the amount of networking code required.
Go ahead and run the application, and assuming you’ve got a network connection, you should see the JSON response logged to the console looking something like this:
I/PostActivity: [{"body":"<p>\r\nWhen developing for the ......
Deserializing the JSON
Alright so we have the JSON loading, but we still want to convert it into Post objects.
Let’s create a new GSON instance back in the onCreate method of our Activity, before calling fetchPosts. We’ll also need to set a custom date format on the GSON instance to handle the dates being returned by the API:
... private Gson gson; @Override protected void onCreate(Bundle savedInstanceState) { ... GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setDateFormat("M/d/yy hh:mm a"); gson = gsonBuilder.create(); fetchPosts(); }
This instance of GSON is what we’ll be using to parse the JSON response within onPostsLoaded like so:
private final Response.Listener<String> onPostsLoaded = new Response.Listener<String>() { @Override public void onResponse(String response) { List<Post> posts = Arrays.asList(gson.fromJson(response, Post[].class)); Log.i("PostActivity", posts.size() + " posts loaded."); for (Post post : posts) { Log.i("PostActivity", post.ID + ": " + post.title); } } };
We deserialize it into an array of Post objects, and convert the array to a List of Post objects for ease of use. Of course you could keep the Post array if you so choose, but for this example we’ll use a List.
Run the application again and you should see output like so:
I/PostActivity: 49 posts loaded. I/PostActivity: 1: Disabling Google Analytics in Development Using Only JavaScript I/PostActivity: 23: Get Started Programming with Python ....
Now we can use our Post objects for whatever the application requires: perhaps animating them into a ListView or caching them in a SQLite Database.
Conclusion
If you go back and compare this implementation with the content of my previous post, you should quickly see how much easier it is to develop networking applications using Volley than using AsyncTask. GSON still remains, in my opinion, the absolute best way to convert between JSON and Java models on Android. Both Volley and GSON are incredibly powerful tools to add to your application, and we’ve barely scratched the surface of what they can do in this post.
The full source code for this post’s application is on GitHub and I welcome any feedback and pull requests to improve the quality of this tutorial.
Hopefully now you’ve gained a better understanding of how GSON and Volley work, and how they can be used to deserialize JSON into Java objects in your own applications.