Parsing nested Json objects in Android
Tags: android, java, retrofit
Published: 2020-06-12 18:59:32

In a perfect world, I would wish that the Apis contain data in a simple format but since this is neither a perfect world nor are wishes horses, this is not always the case. Some use cases justify the need for complex data formats.
<br>
In this blog, we are going to create an android application that parses nested Json Objects from a Web Service and display it on the app.


We will use JSONPlaceholder's fake [API](https://jsonplaceholder.typicode.com/users/1) as our external data source and these two libraries:

1. [Retrofit](https://square.github.io/retrofit/)- a type safe http client for android that we will use to make network calls from our application.
2. [GSON](https://github.com/google/gson)-  A Java serialization/deserialization library to convert JSON Objects into Java.

<br>
<br>
##Project Setup

Create a new project in android studio using the Empty Activity template. When the app has finished building, add these dependencies in `build.gradle` and rebuild your project.

```java
 implementation 'com.squareup.retrofit2:retrofit:2.6.0'
 implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
```

<br>
<br>
##JSONPlaceholder API

This service offers a fake online REST API for Testing and Prototyping. It is a free service hence we don't need an api key to use.
You can visit their [site](https://jsonplaceholder.typicode.com/) for more info about the API.

This is the data we will parse from the [API endpoint](https://jsonplaceholder.typicode.com/users/1), information about a fake user.

```json
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
    }
 },
 "phone": "1-770-736-8031 x56442",
 "website": "hildegard.org",
 "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
       }
 }
```
The first object has 8 items: 
- `"id"`
- `"name"`
- `"username"`
- `"email"`
- `"address"`
- `"phone"`
- `"website"`
- `"company"`

`"address"` and `"company"`are nested objects`. "address"` object has five items with the last item `"geo"` another nested object.
<br>

##Pojos
To successfully parse all those objects, we need to create their subsequent Plain Old Java Objects(Pojo).
To do so, we first create classes that we will use as the blueprints for the pojos.


Let us start with the most nested object(geo).
Create a new Java class and name it UserAdressGeoLocation and populate it with its items.

```java
public class UserAddressGeoLocation {
    @SerializedName("lat")
    private String latitude;

    @SerializedName("lng")
    private String longitude;

    public String getLatitude() {
        return latitude;
    }

    public String getLongitude() {
        return longitude;
    }
}
```
If there are red lines, click on them and hit alt+enter to import the `SerializedName` library.
We use the annotation SerializedName because we changed  the names; `lng` and `lat` with `longitude` and `latitude` respectively. Longitude and latitude are better descriptive variable names, don't you think?
We will use the getters to access the respective items. 
<br>
We move to the next most nested objects, `address` and `company`.

Create a new Java Class, name it UserAddress and populate it with its items.

```java
public class UserAddress {
    private String street;

    private String suite;

    private String city;

    @SerializedName("zipcode")
    private String zipCode;

    private UserAddressGeoLocation geo;

    public String getStreet() {
        return street;
    }

    public String getSuite() {
        return suite;
    }

    public String getCity() {
        return city;
    }

    public String getZipCode() {
        return zipCode;
    }

    public UserAddressGeoLocation getGeo() {
        return geo;
    }
}
```
The last item of this class(geo) is an object of type UserAdressGeoLocation(the class we created in the previous step).

Create another Java class, name it UserCompany and populate it with its items

```java
public class UserCompany {
    private String name;

    private String catchPhrase;

    private String bs;

    public String getName() {
        return name;
    }

    public String getCatchPhrase() {
        return catchPhrase;
    }

    public String getBs() {
        return bs;
    }
}
```

Now we are only left with the least nested object.
Create a new Java class, name it Users and populate it with its items.

```java
public class Users {
    private int id;

    private String name;

    @SerializedName("username")
    private String userName;

    private String email;

    private UserAddress address;

    private String phone;

    private String website;

    private UserCompany company;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getUserName() {
        return userName;
    }

    public String getEmail() {
        return email;
    }

    public UserAddress getAddress() {
        return address;
    }

    public String getPhone() {
        return phone;
    }

    public String getWebsite() {
        return website;
    }

    public UserCompany getCompany() {
        return company;
    }
}
```
The variables `address` and `company` are of the respective class types.

<br>
##The Interface 
We need an Interface to define the endpoint we are getting data from the API.

Creating an interface is similar to creating a class but instead of class, select interface on the drop down in the prompt.
Name the interface JsonPlaceHolderAPI and add the call to get the data.
```java
public interface JsonPlaceHolderAPI {
    @GET("users/1")
    Call<Users> getUsers();
}
``` 
Make sure to implement `Call` from Retrofit2 and `Get`.

[GET](https://www.w3schools.com/tags/ref_httpmethods.asp) is a HTTP method used to request data from a specified resource. `"Users/1"` is the endpoint we are using.

The method getUsers returns an object Call that gets the Json Object of type Users from the endpoint.

##The View
We are next going to modify the `activity_main.xml` by adding an id value to the `Hello World!` TextView

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```
##Tying it all up
With our data model(pojos), interface and xml all set up, let us make the network call and display the data.
```java
public class MainActivity extends AppCompatActivity {
    //The Interface instance
    JsonPlaceHolderAPI placeHolderAPI;
    
    TextView fTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Instantiate the textView
        fTextView = findViewById(R.id.tv1);

        //Building a Retrofit instance
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .addConverterFactory(GsonConverterFactory.create())//Use Gson
                .build();

        //Use the retrofit instance to create the method body of JsonPlaceHolderApi Interface
        placeHolderAPI = retrofit.create(JsonPlaceHolderAPI.class);

        getUser();
    }
    public void getUser(){
        //Execute the Network request
        Call<Users> call = placeHolderAPI.getUsers();
        //Execute the request in a background thread
        call.enqueue(new Callback<Users>() {
            @Override
            public void onResponse(Call<Users> call, Response<Users> response) {
                if (!response.isSuccessful()){
                    fTextView.setText("Code: " + response.code());
                    return;
                }
                if (response.body().getName() != null){
                    //Get the values
                    String userContent = "";
                    userContent += "ID: " + response.body().getId() + "\n";
                    userContent += "Name: " + response.body().getName() + "\n";
                    userContent += "UserName: " + response.body().getUserName() + "\n";
                    userContent += "Email: " + response.body().getEmail() + "\n";
                    userContent += "Street: " + response.body().getAddress().getStreet() + "\n";
                    userContent += "Suite: " + response.body().getAddress().getSuite() + "\n";
                    userContent += "City: " + response.body().getAddress().getCity() + "\n";
                    userContent += "ZipCode: " + response.body().getAddress().getZipCode() + "\n";
                    userContent += "Latitude: " + response.body().getAddress().getGeo().getLatitude() + "\n";
                    userContent += "Longitude: " + response.body().getAddress().getGeo().getLongitude() + "\n";
                    userContent += "Phone: " + response.body().getPhone() + "\n";
                    userContent += "website: " + response.body().getWebsite() + "\n";
                    userContent += "Company Name: " + response.body().getCompany().getName() + "\n";
                    userContent += "CS: " + response.body().getCompany().getCatchPhrase() + "\n";
                    userContent += "Company BS: " + response.body().getCompany().getBs() + "\n";

                    fTextView.setText(userContent);
                }
            }
            @Override
            public void onFailure(Call<Users> call, Throwable t) {
                fTextView.setText("Failure: " + t);
            }
        });
    }
}
```
If there are red lines, click on them and hit alt+enter to import the respective libraries.

##Final piece of the puzzle
Before running the application, we need to add Internet Permission in our Manifest file. 

Add this before `<application>` and as the first line in the manifest
```xml
<uses-permission android:name="android.permission.INTERNET"/>
```

Run the application on a device that is connected to the internet.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/87aw4h2xy5b9srww6tgq.jpg)


The complete code for this application can be found [here](https://github.com/CollinsGichuki/Nested-Json-Objects)