Breaking down data into classes for android

Chris Harte2015 Top Expert (Most Article Points)
CERTIFIED EXPERT
A developer with over twenty years experience. Started on mainframes and Cobol, now on mobiles and Android.
Published:
This is an explanation of a simple data model to help parse a JSON feed


Recently I was faced with a method that consisted of hundreds of lines on very difficult to follow code. The coder was trying to parse all the information from a JSON, assign it to local variables and use it in a textView in a single method. Keeping it to a single method when he started looked like a good idea, but it very quickly got out of hand. All data should be treated as an object to prevent outlandish use of periods and methods.

This one line of code was used to access an integer, a single piece of data;
 
q_result.optJSONObject("results")
                      		.optJSONObject("channel")
                      		.optJSONObject("item")
                      		.getJSONArray("forecast")
                      		.getJSONObject(1)
                      		.optString("code");  

Open in new window



Yes it worked, but there were dozens of lines just like this, all doing pretty much the same thing, all occupying page after page of screen space. Trying to track down what was being used and what else I could use was very trying work. So I started again, this time using discrete classes for each part of the data. Sometimes referred to as models in the MVC framework.
 
This is the source that was being used, a JSON returned by the Yahoo Weather API: 
 http://developer.yahoo.com/weather/


The image above is what it returns, forecast is at the bottom.
 


The API was parsed using an Async method, the result built into a string, this string was then cast as a JSONObject. This object is a copy of the image above stored in memory. This will be passed as an argument to all the classes which will be used to populate the local variables.

This is a quick break down of what is returned and what is wanted:
Everything that can be used is contained in the node 'channel'.
The previous code referenced an index in the node 'forecast'.
The node forecast is an array with five members.
Tomorrows forecast is member []1].
There are six values in this member, three integers (code, high, low) and three strings (date, day, text).
The previous code accessed just one of them, 'code'.

Starting with the required data, and working our way up the tree, the node forecast is the place to begin. Create a class called Forecast, the name is arbitrary but it will only ever be used to access the 'forecast' node and will be a reminder of what it is. Declare local variables to match the required values ('code' and 'text') and Eclipse (or Android Studio) will helpfully provide the getters. There is no need for setters, the data will be gleaned from the API.

This is the forecast class:
 
public class Forecast
                      {
                          private int code; 
                          private String text;
                          
                          
                          public int getCode()
                          {
                              return code;
                          }
                      
                          public String getText()
                          {
                              return text;
                          }       
                      }

Open in new window






Since setters are not being used, a method is going to be added to this class to populate the variables. Because forecast is an array the parameter will have to be a JSONArray. This will need to be in a try/catch or throw an exception to compile. Remember, the whole array is not needed, just the values for tomorrow(1). This method, when called, will be passed the JSONObject as a parameter.
 
public void populate (JSONArray data) throws JSONException
                      {
                         code = data.getJSONObject(1).optInt("code");       
                         text = data.getJSONObject(1).optString("text");//The name of the index in the forecast array               
                              
                      } 

Open in new window






The image of the JSON above shows the array 'forecast' to be a member of the node 'item'. That means there is a need for a class for the Item node. This will have a local variable of type Forecast, a getter and a populate. There is a field in item called 'title' that is going to be useful at some point in the future so it is included. Again when the method is called it will be passed the JSONObject as a parameter.
 
public class Item 
                      {
                          private Forecast forecast;
                          private String title; //This is in the item node, you will want it at some point
                      
                          
                          public Forecast getForecast()
                          {
                              return forecast;
                          }  
                        
                          public String getTitle()
                          {
                              return title;
                          } 
                          
                      
                          public void populate(JSONObject data) throws JSONException
                          {        
                              forecast = new Forecast();
                              forecast.populate(data.getJSONArray("forecast"));//The name of the node in the JSON
                      
                      	  title = data.optString("title");
                              
                          }        
                      }

Open in new window



Following the JSON tree up and Item exists inside 'channel', so a repeat of the last procedure with Item as the local variable. Spot the parameter:



public class Channel 
                      {        
                          private Item item;
                      
                          public Item getItem()
                          {
                              return item;
                          }    
                          
                          
                          public void populate(JSONObject data) throws JSONException
                          {
                              
                              item = new Item();
                              item.populate(data.optJSONObject("item"));        
                              
                          }
                          
                      }

Open in new window






This looks like a huge amount of work for not much return, but bear with me, it will pay off. The original JSON was accessed in an Async, in the onPostExecute method populate the channel class from the JSONObject.

@Override protected void onPostExecute(String result)            
                      {                  
                           try
                           {                                    
                               JSONObject data = new JSONObject (result);
                                      
                               JSONObject q_result = data.optJSONObject("query");                                        
                                          
                               Channel chan = new Channel();
                               chan.populate(q_result.optJSONObject("results").optJSONObject("channel"));                    
                       
                               main_activity.feed_success(chan);//This is where we use the data
                                          
                            }
                            catch (JSONException e)
                            {
                                 main_activity.feed_failure(e);
                            } 
                      }

Open in new window






This will populate the class Channel which will populate the class Item which will populate the class Forecast.
In the feed_success method, called by the onPostExecute, we can now instantiate all the classes and access the information in them.

   public void feed_success(Channel channel) throws JSONException
                          {                
                              Item         item = channel.getItem();
                              Forecast forecast = item.getForecast();
                              
                              String forecast_date = forecast.getDate();
                              String forecast_forc = forecast.getText();                               
                                   
                              
                              text_view.setText("Tomorrow, " + " " + forecast_date + " will be\n" + forecast_forc);                
                      
                          }

Open in new window






This sets a text view with two strings. That is a lot of work for just two variables, but compare the first four lines of this method with these two lines of code:
 
        String forecast_date = channel.optJSONObject("results").optJSONObject("channel").optJSONObject("item").getJSONArray("forecast").getJSONObject(1).optString("code");
                              String forecast_forc = channel.optJSONObject("results").optJSONObject("channel").optJSONObject("item").getJSONArray("forecast").getJSONObject(1).optString("text");

Open in new window






When the need to use all six values in forecast arises, as it will, only the class needs to be added to. The data in the JSON is all read and parsed, so use it. Add the extra variables to Forecast so it looks like this:

public class Forecast
                      {
                          private int code;
                          private String date;
                          private String day;
                          private int high;
                          private int low;
                          private String text;
                          
                          
                          public int getCode()
                          {
                              return code;
                          }
                          public String getDate()
                          {
                              return date;
                          }
                          public String getDay()
                          {
                              return day;
                          }
                          public int getHigh()
                          {
                              return high;
                          }
                          public int getLow()
                          {
                              return low;
                          }
                          public String getText()
                          {
                              return desc;
                          }
                          
                          public void populate (JSONArray data) throws JSONException
                          {
                              code = data.getJSONObject(1).optInt("code");       
                              date = data.getJSONObject(1).optString("date");
                              day = data.getJSONObject(1).optString("day");
                              high = data.getJSONObject(1).optInt("high");
                              low = data.getJSONObject(1).optInt("low");
                              text = data.getJSONObject(1).optString("text");               
                              
                          }
                          
                      }

Open in new window






That is it, the information in 'forecast' is now all available, you have just moved it from the parsed JSON to this class. Now give every node its own class and every time you instantiate the Channel class all the information will be neatly divided up and easily accessible.

This is just using eleven variables from the whole feed, imagine the same thing using the direct reference that was first employed.
   public void feed_success(Channel channel) throws JSONException
                          {
                              
                              Item         item = channel.getItem();
                              Wind         wind = channel.getWind();
                              Astronomy  astron = channel.getAstro();
                              Units       unit  = channel.getUnits();
                              Condition    cond = item.getCond();
                              Forecast forecast = item.getForecast();
                              
                              
                              String units   = unit.getTemp();
                              String sunrise = astron.getSunrise();
                              String sunset  = astron.getSunset();
                              
                              String chill = wind.getChill();
                              String speed = wind.getSpeed();
                              
                              int cond_code = cond.getCode();
                              int temp      = cond.getTemp();
                              String desc   = cond.getDesc();
                              
                              String forecast_date = forecast.getDate();
                              String forecast_day = forecast.getDay();
                              String forecast_forc = forecast.getDesc();

Open in new window






This is data driven code and an example of why not only code, but data should be subject to objectification.



0
1,741 Views
Chris Harte2015 Top Expert (Most Article Points)
CERTIFIED EXPERT
A developer with over twenty years experience. Started on mainframes and Cobol, now on mobiles and Android.

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.