Showing posts with label flexjson. Show all posts
Showing posts with label flexjson. Show all posts

9/23/2010

Flexjson meet Android

Flexjson 2.1 now supports running Flexjson on Android. So I thought I'd show a quick example of using Flexjson in an Android application. Hopefully this will spark some ideas about what you can use Flexjson for in your own application. I'm going to start simple creating a quick Android app that pulls recipes from Puppy Recipe, parses it using Flexjson, and displays it in a list. Let's get started.

Recipe Puppy has a very simple REST API, almost too simple, that returns responses in JSON. Recipe puppy allows you to search recipes by the ingredients contained within by using a URL parameter i. Individual ingredients are separated by a comma, and URL encoded. Here is a simple example:

http://www.recipepuppy.com/api/?&i=banana,chicken&p=1

Exciting isn't it? If you click that link you'll see the JSON response. It's a little hard to read like that so here is a simple break down with a little formatting:


{
"title":"Recipe Puppy",
"version":0.1,
"href":"https:\/\/blue-sea-697d.quartiers047.workers.dev:443\/http\/www.recipepuppy.com\/",
"results":[
{
"title":"Chicken Barbados \r\n\r\n",
"href":"https:\/\/blue-sea-697d.quartiers047.workers.dev:443\/http\/www.kraftfoods.com\/kf\/recipes\/chicken-barbados-53082.aspx",
"ingredients":"chicken, orange zest, chicken, banana, orange juice, brown sugar, flaked coconut",
"thumbnail":"https:\/\/blue-sea-697d.quartiers047.workers.dev:443\/http\/img.recipepuppy.com\/602538.jpg"
},
...
]
}


This is pretty straight forward. We have a little header and what we really are interested in results property which is an array of recipe objects. So we'll create two simple Java classes to map those data members. RecipeResponse for the header portion, and Recipe which is the object contained within "results" property.

Here are those objects:


public class RecipeResponse {
public String title;
public Double version;
public String href;
public List<Recipe> results;

public RecipeResponse() {
}
}

public class Recipe {

private String title;
private String href;
private String ingredients;
private String thumbnail;
private Drawable thumbnailDrawable;

public Recipe() {
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title.trim();
}

}


In the Recipe object I actually created a Java Bean with getter/setter, but I didn't include most of those methods. I did make a point to show the setter for the title property. Turns out some of the data coming out of recipe puppy contains extra newlines characters in the title. To get rid of those I'm doing a trim() in the setter. Flexjson is smart enough to call the setter method if you have defined it instead of setting values directly into the instance variables. However, if you use public instance variables it will set values directly into those too. This was a fix made in 2.1 with respect to using public instance variables during deserialization process. You'll be happy to know it works now.

So let's jump to the usage of Flexjson in the android code. So we create a RecipeActivity that contains a List to display the recipes. We're going to look at the AsyncTask that loads the data using Flexjson. Here is the full code for that:


new AsyncTask=<String, Integer, List<Recipe>>() {

private final ProgressDialog dialog = new ProgressDialog(RecipeActivity.this);

@Override
protected void onPreExecute() {
dialog.setMessage("Loading Recipes...");
dialog.show();
}

@Override
protected List<Recipe> doInBackground(String... strings) {
try {
return getRecipe( null, 1, "banana", "chicken" );
} catch( IOException ex ) {
Log.e( RECIPES, ex.getMessage(), ex );
return Collections.emptyList();
}
}

@Override
protected void onPostExecute(List<Recipe> results) {
if( dialog.isShowing() ) {
dialog.dismiss();
}
Log.d( RECIPES, "Loading " + results.size() + " Recipes" );
recipes.setList( results );
new ThumbnailLoader( recipes ).execute( recipes.toArray( new Recipe[ recipes.size() ]) );
Log.d( RECIPES, "Loaded " + recipes.size() + " Recipes" );
}

protected List<Recipe> getRecipe( String query, int page, String... ingredients ) throws IOException {
String json = HttpClient.getUrlContent( String.format( "http://www.recipepuppy.com/api/?q=%s&i=%s&p=%d",
query != null ? URLEncoder.encode(query) : "",
ingredients.length > 0 ? URLEncoder.encode(join(ingredients,",")) : "",
page ) );
RecipeResponse response = new JSONDeserializer<RecipeResponse>().deserialize(json, RecipeResponse.class );
return response.results;
}
}.execute();


The method your probably most interested in is getRecipe(). This method formats the URL we're going to load. It then loads that URL and passes the results returned as a JSON block to the JSONDeserializer. JSONDeserializer will take a JSON formatted String and bind that into a Java object. In this example, we're binding into a RecipeResponse object. Here is how that is done:


RecipeResponse response = new JSONDeserializer<RecipeResponse>().deserialize(json, RecipeResponse.class );


A single line of code does that. The deserialize() method performs the deserialization and binding. The first argument is the JSON String, and the second is the top level class we want to bind into. Notice we didn't have to mention anything about Recipe. Flexjson is smart enough to use the data types from the top level object to figure out any other data types contained within. So if you refer to the RecipeResponse.results instance variable you can see the List data type with a generic type. Flexjson will use generics whenever possible to figure out concrete types to instantiate. Of course polymorphism, interfaces, abstract classes, and the like causes issues with this, but we're not going into that right now. See the Flexjson home page to find out more.

You'll notice the RecipeResponse object is returned fully populated with the JSON data, but we're really only interested in response.results so we just return that. It'd be nice if Recipe Puppy returns how many total pages there were in the header (hint, hint) so that it was more interesting. Anyway it is beta. That array is then added to the ListAdapter and displayed on the screen.

Other things Flexjson could be used for is saving state by serializing objects to JSON, and then deserializing when Activities are reconstituted. This can be easier than writing ContentProviders to dump stuff into the database. One of my biggest gripes with Android is how between pages objects can be reliably sent because Intent's require you break everything down to primitives. With Flexjson we can just simply serialize an object put that in the Intent, and then deserialize it on the other side. So no more boilerplate code to flatten your objects.

Here's a simple example serializing our recipes to the disk:


File f = app.getFilesDir();
Writer writer = new BufferedWriter( new FileWriter( new File( f, "recipes.json") ) );
try {
new JSONSerializer().deepSerialize(favorites, writer);
writer.flush();
} finally {
writer.close();
}


Now I know there are people worried about performance, but timing the following code this ran on device in less than 40ms which is within the acceptable bounds for UI performance. If you need more performance you can cache the JSONSerializer/JSONDeserializer instance which optimizes data type mappings so it doesn't recompute those when serializes and deserializes. As always measure, measure, measure.

You've gotten an introduction about how Flexjson can make it easier to work with JSON data with Android.

9/26/2007

Flexjson 1.5 is live!

After a while I've finally released Flexjson 1.5 to the world. I've been running it for quite sometime trying to see how I liked some of the changes. Since I haven't wanted to change any of my decisions I thought that meant it was time to let other people try it out. There were some big additions to the library. The primary was adding wildcard support for including and excluding fields from your objects. This means you can now exclude one or more fields by using the star notation. The biggest example would be to omit the class attribute on JSON objects. For example:


new JSONSerializer().exclude("*.class").serialize( obj );


Pretty simple huh? You can even use wildcards more than once so expressions like:


new JSONSerializer().exclude("foo.*.bar.*").prettyPrint( obj );


Using a plain star ('*') will cause a deep serialization. In previous releases you used the deepSerialize() method to get a deep serialization. You can still use that method. The big thing that changed between releases is the evaluation order of includes and excludes. In prior releases includes were always processed before excludes. So that meant if you did the following:


new JSONSerializer().exclude("*.class").include("my.hobbies").serialize( obj );


In previous release "my.hobbies" would always be processed before *.class, but now they are processed in the order in which you register them. So in 1.5 the order would be to evaluate each field against the "*.class" exclude then evaluate it against "my.hobbies". This enables you do things like:


new JSONSerializer().exclude("foo.phoneNumbers").include("*").prettyPrint( obj );


You might notice a new method I'm using...prettyPrint(). Pretty print is fairly straight forward it formats your JSON output in a pretty to read format. Very nice for debugging.

The other big feature is Transformers. Transformers allow you to register classes that participate in serializing fields. It's best understood as described by a use case. Say we have an object that represents an email, and we want to send it to the browser over JSON. But, emails can have illegal characters for HTML like < or > in fields like the to, from, or cc portions. Before we would have to create a separate method that would HTML encode those characters, and exclude the method that returned those values in plain text. Now we can register a transformer for those fields on the fly. Here's how we can solve this problem:


new JSONSerializer().transform( new HTMLEncoder(), "to", "from", "cc" ).prettyPrint( email );


So the transform() method allows us to register a Transformer called HTMLEncoder. The "to", "from", and "cc" are fields within the email object that we want to run this Transformer on. We can register this Transformer with one or more fields to make it easy on us. The transform() method supports dot notation, just like include and exclude, but doesn't support wildcards.

Transformers can do all sorts of things. Say transform Markdown into HTML from your objects, escape HTML tags and script tags to protect your application, or translating dates into non-numeral format (2007-08-12) or (1/1/2008). Flexjson ships with a HTMLEncoder that you can use out of the box. In the future I hope to add more, especially for security concerns.

This release also has several bug fixes, and performance enhancements. There are some very exciting features in this release. Grab it and see how you like it.