Parsing JSON in Android With Gson: A Practical Guide

Mobile developers across Sydney, Melbourne, and Brisbane increasingly rely on RESTful APIs to power their apps. Parsing the JSON payloads these services return quickly and reliably is a core skill. The Gson library remains one of the most accessible tools for this job, offering a streamlined way to map raw text into structured objects.

Whether you are building a fintech startup in Surry Hills or crafting a transit app for Melbourne's tram network, you will likely need to handle JSON responses in some form. Gson handles everything from nested objects to primitive arrays, and works with both Java and modern Kotlin code. This walkthrough covers the practical steps an Australian developer needs to get Gson working in a project, parse common data shapes, and avoid common traps.

Gson sits on Maven Central, ready to drop into a Gradle build file in seconds. The Android Studio environment used by most local developers mirrors the setups in shared offices from Perth to Canberra. Once configured, the library handles much of the heavy lifting through reflection, freeing you to focus on business logic rather than manual string parsing.

Adding Gson to Your Gradle Build

Declare Gson as a dependency in the module-level build.gradle file. Add the implementation line inside the dependencies block. The latest stable release works with Android API 19 and above, which covers virtually every handset in use across Australia.

implementation 'com.google.code.gson:gson:2.10.1'

After saving, sync your project with Gradle. Android Studio pulls the library from Maven Central and makes the classes available throughout your codebase. Many local devs find that Android tutorial point covers Gradle basics neatly, so it is worth bookmarking alongside other beginner topics as you build out a broader learning path.

Modelling Your JSON With Data Classes

Before deserialising a JSON payload, you need a class that mirrors its structure. In Kotlin, this is typically a data class with properties whose names and types match the JSON fields. In Java, a plain POJO with private fields, getters, and a no-arg constructor works just as well.

Suppose the API returns weather observations from the Bureau of Meteorology for a capital city like Adelaide or Hobart. A typical JSON object includes temperature, humidity, wind speed, and a timestamp. The matching Kotlin class might look like this:

data class WeatherReading(
    val temperature: Double,
    val humidity: Int,
    val windSpeed: Double,
    val timestamp: String
)

Field types matter. Gson attempts to coerce values when reasonable, but mismatched types cause runtime exceptions. If a field can be absent, use the nullable variant or Java boxed types so Gson can populate it with null rather than throwing.

Deserialising Simple JSON Objects

With the dependency in place and a class ready, parsing happens through the Gson instance. The default constructor produces a Gson object configured with sensible defaults. Call fromJson, passing the JSON string and target class, and you receive a populated instance.

val gson = Gson()
val json = "{\"temperature\":22.5,\"humidity\":65,\"windSpeed\":12.0,\"timestamp\":\"2024-01-15T10:00:00Z\"}"
val reading = gson.fromJson(json, WeatherReading::class.java)

This pattern covers the vast majority of single-object responses. Many Australian services, from ride-share platforms in Darwin to retail loyalty programs, return flat structures that fit this shape. If your JSON arrives as an InputStream, use the Reader overload and wrap the stream in an InputStreamReader. This avoids loading large payloads into memory on regional mobile networks where bandwidth dips.

Handling Nested Objects and Arrays

Real-world responses rarely stay flat. They contain nested objects, lists, and occasionally deeply layered structures. Gson handles these by following the same rules recursively: every nested class needs its own model, and every list needs an explicit type token.

Consider an API that returns a list of surf reports for beaches along the NSW coast, where each entry includes the beach name and an array of tide times. Define a TideTime class for the inner objects, and a wrapper that holds the list. To deserialise the array, use TypeToken from the Gson reflect package:

val listType = object : TypeToken<List<TideReport>>() {}.type
val reports = gson.fromJson(jsonString, listType)

The TypeToken trick captures the generic type at compile time because Java erases generic information at runtime. Without it, Gson has no way of knowing what kind of objects belong in the list and falls back to LinkedTreeMap, which is rarely what you want.

Customising Field Names and Ignoring Fields

API designers do not always use field names that match Java or Kotlin conventions. Sometimes a JSON key uses snake_case, or the upstream team renames a field without warning. Gson offers the @SerializedName annotation to bridge the gap.

data class Customer(
    @SerializedName("first_name") val firstName: String,
    @SerializedName("last_name") val lastName: String,
    @SerializedName("loyalty_tier") val loyaltyTier: String? = null
)

You can also mark fields as transient or annotate them with @Expose to control what Gson includes during serialisation. When generating JSON for outbound requests, this is useful for stripping sensitive values before they leave the device. Under Australia's Privacy Act 1988 and the Notifiable Data Breaches scheme, handling personal data responsibly is not just good practice but a legal expectation, so being explicit about what gets serialised helps meet compliance goals.

Real-World Example: Parsing an Australian Weather API

Putting everything together, imagine you are building an app that pulls the latest observations from the Bureau of Meteorology for a user in Perth or Brisbane. The endpoint returns a JSON envelope with metadata and a data array. The envelope class holds a list of readings, and each reading uses the model from earlier.

Create a request using Retrofit, OkHttp, or even HttpURLConnection. Once you receive the response body as a string, hand it to Gson with the right TypeToken. Wrap the call in a coroutine or background thread, since touching the network on the main thread throws a NetworkOnMainThreadException on modern Android versions.

suspend fun fetchWeather(): List<WeatherReading> {
    val response = httpClient.get(WEATHER_URL)
    val type = object : TypeToken<WeatherEnvelope>() {}.type
    val envelope = gson.fromJson(response.body!!.string(), type)
    return envelope.observations
}

Common Pitfalls and Performance Tips

Gson uses reflection under the hood, which carries a small startup cost. For most apps this is negligible, but if you parse thousands of objects in a tight loop, cache the Gson instance and reuse it. Creating a new Gson object for every parse call is a common rookie mistake that wastes memory.

ProGuard and R8 can strip the model classes during minification unless you keep them explicitly. Without keep rules, deserialisation fails with cryptic "no field" errors at runtime. Add rules like -keep class com.example.app.models.** { *; } to your proguard-rules.pro file.

Watch out for date formats. Gson parses dates as java.util.Date by default, but the actual format varies between APIs. Some return ISO 8601 strings, others use epoch milliseconds. Register a GsonBuilder with a custom date format or set leniency flags so the parser tolerates minor variations.

Build a tiny sample app that fetches a public Australian API, parses it with Gson, and displays the result in a simple list. Once that flow feels natural, the same approach scales to larger codebases. Drop into the documentation, try a few real endpoints today, and ship a release before the end of the week.