Parsing JSON
You have an HTTP response body — a string of JSON text. Now you need Java objects. Doing this by hand with JSONObject is tedious and error-prone; every field access is a getString() call and a potential JSONException. Industry Android development uses a binding library that maps JSON keys to Java fields automatically. The two dominant choices are Gson (Google) and Moshi (Square). This lesson covers both so you can make an informed choice and read any codebase you encounter.
Why a Binding Library?
Manual parsing couples your code to JSON structure. Change a key name in the API and you must hunt down every getString("old_key") call. A binding library centralises that mapping in a single model class. The library uses reflection (Gson) or code generation (Moshi) to populate your objects — you write a class, annotate it, and let the library do the rest.
Serialisation vs Deserialisation: Deserialisation is JSON → Java object (what you do most with API responses). Serialisation is Java object → JSON string (what you do when sending a request body). Both libraries handle both directions.
Adding the Dependencies
In your module-level build.gradle:
dependencies {
// Gson — reflection-based, no annotation processor needed
implementation 'com.google.code.gson:gson:2.10.1'
// Moshi — code-generation-based (faster, null-safe)
implementation 'com.squareup.moshi:moshi:1.15.1'
implementation 'com.squareup.moshi:moshi-kotlin:1.15.1' // optional KotlinJsonAdapterFactory
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.15.1' // for @JsonClass(generateAdapter=true)
// For pure-Java projects use the reflection adapter instead of kapt
}
Pure-Java Android projects: Moshi's code-generation works through annotation processing. If you are not using Kotlin, replace kapt with annotationProcessor and use MoshiJsonAdapterFactory from the moshi-adapters artifact, or simply use Gson — it requires no annotation processor at all.
Modelling the JSON
Suppose the REST API returns a user profile:
{
"id": 42,
"full_name": "Layla Hassan",
"email": "layla@example.com",
"is_active": true,
"score": 98.5
}
Create a plain Java class (a POJO) that mirrors this shape:
// User.java
public class User {
public int id;
public String fullName; // will be mapped with a @SerializedName / @Json annotation
public String email;
public boolean isActive;
public double score;
}
Parsing with Gson
Gson maps JSON keys to Java field names automatically when they match. For snake_case keys that differ from camelCase field names, use @SerializedName:
import com.google.gson.annotations.SerializedName;
public class User {
public int id;
@SerializedName("full_name")
public String fullName;
public String email;
@SerializedName("is_active")
public boolean isActive;
public double score;
}
Deserialise a single object from a JSON string:
import com.google.gson.Gson;
String json = "{ \"id\": 42, \"full_name\": \"Layla Hassan\", "
+ "\"email\": \"layla@example.com\", \"is_active\": true, \"score\": 98.5 }";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
System.out.println(user.fullName); // Layla Hassan
System.out.println(user.isActive); // true
For a JSON array at the top level, use TypeToken to carry the generic type information that Java erases at runtime:
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
String jsonArray = "[ {\"id\":1, ...}, {\"id\":2, ...} ]";
Type listType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(jsonArray, listType);
System.out.println(users.size()); // 2
Serialise back to JSON just as easily:
String output = gson.toJson(user);
// {"id":42,"fullName":"Layla Hassan","email":"layla@example.com","isActive":true,"score":98.5}
Gson ignores missing fields silently. If the JSON does not contain a key, the corresponding Java field is left at its default (0, false, null). This can hide API contract changes. Always validate critical nullable fields before use.
Parsing with Moshi
Moshi's API is similar but uses @Json for name mapping and requires an adapter per type:
import com.squareup.moshi.Json;
public class User {
public int id;
@Json(name = "full_name")
public String fullName;
public String email;
@Json(name = "is_active")
public boolean isActive;
public double score;
}
import com.squareup.moshi.Moshi;
import com.squareup.moshi.JsonAdapter;
import java.io.IOException;
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<User> adapter = moshi.adapter(User.class);
User user = adapter.fromJson(json);
System.out.println(user.fullName); // Layla Hassan
// Serialise
String output = adapter.toJson(user);
For a list:
import com.squareup.moshi.Types;
import java.lang.reflect.ParameterizedType;
import java.util.List;
ParameterizedType type = Types.newParameterizedType(List.class, User.class);
JsonAdapter<List<User>> listAdapter = moshi.adapter(type);
List<User> users = listAdapter.fromJson(jsonArray);
Moshi throws on null violations. If a non-nullable field in the adapter receives a JSON null, Moshi throws JsonDataException. This strict behaviour surfaces API bugs early — one reason developers prefer Moshi for new projects.
Nested Objects and Arrays
Real APIs nest objects. Gson and Moshi both handle nesting automatically as long as your Java model mirrors the JSON hierarchy:
// JSON
// { "id": 1, "author": { "id": 10, "full_name": "Sara Ali" }, "tags": ["android","java"] }
public class Post {
public int id;
public User author; // nested object
public List<String> tags; // array of primitives
}
// Parsing is identical — Gson and Moshi recurse automatically
Post post = gson.fromJson(json, Post.class);
System.out.println(post.author.fullName); // Sara Ali
System.out.println(post.tags.get(0)); // android
Where to Parse in an Android App
JSON parsing is CPU work — it must never run on the main (UI) thread. In Lesson 4 you learned about background threading. When using HttpURLConnection (Lesson 5) or Retrofit (Lesson 6), the parsing call belongs inside your AsyncTask, Callable, or enqueue callback — all of which already execute off the UI thread. If you use Retrofit with a GsonConverterFactory, parsing happens automatically inside Retrofit's OkHttp thread pool and you receive a ready-made Java object in your callback.
// Retrofit + Gson integration (build.gradle)
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// RetrofitClient.java
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
// Your API interface
public interface UserApi {
@GET("users/{id}")
Call<User> getUser(@Path("id") int id);
}
// Usage — Retrofit deserialises User automatically
UserApi api = retrofit.create(UserApi.class);
api.getUser(42).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User user = response.body(); // already parsed
runOnUiThread(() -> textView.setText(user.fullName));
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
Log.e("API", "Request failed", t);
}
});
Gson vs Moshi — Which to Choose?
- Gson: Simpler setup, no annotation processor, widely documented, works with reflection even without annotations. Good default for straightforward projects.
- Moshi: Faster (code-gen adapter), null-safe by default, smaller runtime, integrates well with Kotlin. Preferred for new projects and large codebases where correctness matters more than convenience.
- Both: Have first-class Retrofit converter factories, handle nested objects and lists, and support custom type adapters for special types like dates.
Summary
Binding libraries eliminate hand-written JSON parsing. Create a POJO that mirrors your API's JSON shape, annotate mismatched field names with @SerializedName (Gson) or @Json (Moshi), then let the library deserialise and serialise for you. Keep parsing off the UI thread — in practice, let Retrofit's converter factory handle it so your callback receives clean, ready-to-use Java objects. In the next lesson you will put all of this together when consuming a full REST API end-to-end.