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.
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 as our external data source and these two libraries:
- Retrofit- a type safe http client for android that we will use to make network calls from our application.
- GSON- A Java serialization/deserialization library to convert JSON Objects into Java.
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.
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
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 for more info about the API.
This is the data we will parse from the API endpoint, information about a fake user.
{
"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.
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.
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.
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.
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
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.
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.
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.
public interface JsonPlaceHolderAPI {
@GET("users/1")
Call<Users> getUsers();
}
Make sure to implement Call
from Retrofit2 and Get
.
GET 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 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.
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
<uses-permission android:name="android.permission.INTERNET"/>
Run the application on a device that is connected to the internet.
The complete code for this application can be found here
Top comments (2)
hey man thank you for great explaining , can you help me on how will be the java model and the retrofit api interface for the following nested JSON code :
//___________________________________//
{
"error":false,
"status":"success",
"locationData":[
{
"image":"https:\/\/google.com\/style\/images\/profile\/company.jpg",
"id":"2486",
"name":"3L INTERNATIONAL SPRL",
"latitude":"50.660725 ",
"longitude":"4.632701",
"addressOne":"Rue du Bosquet 7 ",
"addressTwo":"1348 Louvain LaNeuve",
"state":"",
"avgRating":"3.0",
"rattingValues":[
{
"rattingLable":"1",
"rattingValue":"0",
"ratingPercentage":"0"
},
{
"rattingLable":"2",
"rattingValue":"0",
"ratingPercentage":"0"
}
],
"totalReview":"4",
"totalRatingCount":"4",
"country":"Belgium",
"userLocation":"Belgium",
"companyType":"Trader",
"category":"0",
"newJoined":false
}
]
}
this helped thanks ;)