2014年3月19日 星期三

Using Jackson to unmarshall Yahoo Weather RESTful web serice for Android Restful Client App


    While developing an Android application, it is mandatory that we may work with web services or SOA applications by exchanging XML or JSON data. Comparing to XML, JSON has been the preferred format, because it is much more lightweight. We will first consider about the JSON to Java object conversion. To unmarshall JSON document, the most popular pre-processors are Jackson and GSON. After some googling, I decided to use Jackson in my example to get JSON from Yahoo Weather, due to the better features and performance of Jackson. You may visit the discussion about Jackson Vs GSON on Stackoverflow.

    To use Jackson to unmarshall JSON document, you may follow steps as below:

Step1: Download jars of Jackson core module from Jackson and add reference for the jars in Eclipse
       Jackson core modules are the foundation on which extensions (modules) build upon. These are three and they are known as:
    Streaming ("jackson-core-2.2.3.jar") defines low-level streaming API, and includes JSON-specific implementations
    Annotations("jackson-annotations-2.2.3.jar") contains standard Jackson annotations
    Databind ("jackson-databind-2.2.3.jar") implements data-binding (and object serialization) support on streaming package; it depends both on streaming and annotations packages

        After adding the reference, you should be able to see them in Package Explorer.

Step2: Generate POJO from JSON schema or data
      POJO classes can be generated by tools. You may visit http://www.jsonschema2pojo.org and you can paste JSON schema or data into the left hand side textarea and name the package and class names. 

  Yahoo Weather Forecast
     To get weather forecast from Yahoo Weather YQL API, you can paste below URL in browser and you should be able to get a weather JSON data from Yahoo.  Well, yep, it's a SQL-like stuff. You can specify the preferred location, preferred unit(Celsius or Fahrenheit) and returned format to look up as below:
   http://query.yahooapis.com/v1/public/yql?q=select%20item%20from%20weather.forecast%20where%20location%3D%22TWXX0025%22%20and%20u=%22c%22&format=json
    

       You may wonder where you can get the location codes. No worry. To get the location code, please visit web site as below. You can find location codes for most of countries. The location codes are used for AOL weather, weather.com and Yahoo weather.



   JSON Formatter & Validator
         JSON formatter & validator can help you to validate whether the JSON data is well-form and provide you a better format to read.
         Please visit http://jsonformatter.curiousconcept.com for detail.

   jsonschema2pojo
        To generate POJO from JSON, you can do it manually, if the model is simply a java class. However, if it's complicated enough, I would think it's the better to generate them via tool and modify them accordingly. To my example, I actually didn't do any modification after code generation.
       The GUI of the web site is very simple. You can paste the JSON schema or data into the left hand side text area and press your preferred package name and class name. It's nice the tool can generate POJO for Jackson and GSON and even add more annotation styles for Joda and others.



    After pressing Preview button, it prompts a preview dialog to see the generated POJO classes. If you like the generated result, you can click Jar button to get the jar file with the POJO source code. 


    YahooWeather-source.jar can be download after pressing Jar button.   

  I decompress the jar file and move them to my project. If you like to download my POJO from jsonschema2pojo, you can download from here.


Step3: Use ObjectMapper to unmarshall JSON string from Yahoo Weather
      To use Jackson is simple. There're just few lines of code. 
ObjectMapper mapper = new ObjectMapper();
try {
 YahooWeather ym = mapper.readValue(jsonString, YahooWeather.class);
} catch (JsonGenerationException e) {
 Log.d("RestingTask", "Resting JsonGenerationException: " + e);
} catch (JsonMappingException e) {
 Log.d("RestingTask", "Resting JsonMappingException: " + e);
} catch (IOException e) {
 Log.d("RestingTask", "Resting IOException: " + e);
}


   Add an AsyncTask for my button action
package com.hitech.weather.task;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hitech.weather.model.YahooWeather;
import com.hitech.weather.model.Forecast;

public class RestingAsyncTaskUnmarchall extends AsyncTask<String, String, String> {
 TextView tv = null;

 public RestingAsyncTaskUnmarchall(TextView tv) {
  this.tv = tv;
 }

 @Override
 protected String doInBackground(String... uri) {
  HttpClient httpclient = new DefaultHttpClient();
  HttpResponse response;
  String responseString = null;
  try {
   response = httpclient.execute(new HttpGet(uri[0]));
   StatusLine statusLine = response.getStatusLine();
   if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    response.getEntity().writeTo(out);
    out.close();
    responseString = out.toString();
    
    responseString = unmarchallJson(responseString);
   } else {
    // Closes the connection.
    response.getEntity().getContent().close();
    throw new IOException(statusLine.getReasonPhrase());
   }
  } catch (ClientProtocolException e) {
   Log.d("RestingTask", e.getMessage());
  } catch (IOException e) {
   Log.d("RestingTask", e.getMessage());
  }

  Log.d("RestingTask", responseString);
  return responseString;
 }

 @Override
 protected void onPostExecute(String result) {
  super.onPostExecute(result);
  // Do anything with response..
  tv.setText(result);
 }

 private String unmarchallJson(String jsonString) {
  ObjectMapper mapper = new ObjectMapper();
  StringBuffer sb = new StringBuffer();

  try {
   // read from file, convert it to user class
   YahooWeather ym = mapper.readValue(jsonString, YahooWeather.class);
   // display to console
   //System.out.println(example);
   sb.append("counts:");
   sb.append(ym.getQuery().getCount());
   sb.append("title:");
   sb.append(ym.getQuery().getResults().getChannel().getItem().getTitle());
   sb.append("whether:");
   List<Forecast> forecastList = ym.getQuery().getResults().getChannel().getItem().getForecast();
   for (int i=0; i<forecastList.size(); i++) {
    Forecast forecast = forecastList.get(i);
      sb.append("," + forecast.getDate() + ":(" + forecast.getLow() + "~" + forecast.getHigh() + "," + forecast.getText() + ")"); 
   }
   
  } catch (JsonGenerationException e) {
   Log.d("RestingTask", "Resting JsonGenerationException: " + e);
  } catch (JsonMappingException e) {
   Log.d("RestingTask", "Resting JsonMappingException: " + e);
  } catch (IOException e) {
   Log.d("RestingTask", "Resting IOException: " + e);
  }
  return sb.toString();
 }
}



Step4: Enjoy it!     


沒有留言: