Tips for Android JSON Parsing Using Jackson
26 May 2013At Smule, we use the Jackson library for our JSON parsing. Marshaling JSON into corresponding model objects is elegant and easy with Jackson and makes code much more readable. Here are some tips for those considering using Jackson for their marshaling.
Update: This post applies to Jackson 1.9. If you’re using Jackson 2.0, please see my post on upgrading from Jackson 1.9 to 2.0.
To parse JSON, you can use Jackson’s treeToValue method. This method throws some exceptions so it’s recommended you create a helper method that catches these exceptions instead of having try-catches everywhere you are parsing (our JSON helpers are in a class called JsonUtils). Jackson best practices recommends one re-use object mappers since creation of the mapper is an expensive operation - so our JSON helper class has a defaultMapper() method.
Model classes should usually ignore unknown JSON properties. If a server API later adds new fields or we just don’t care about some fields, the parser won’t throw exceptions if they are ignored. Jackson also requires a defined default constructor, otherwise an exception will be thrown on parsing.
Model classes can annotate variables to automatically parse JSON fields. This works automatically for primitive types and Strings, but it can even parse other classes if those classes are set up for Jackson parsing.
If you have data nested in an array or hash and want to get at the data in it, you have to override the setter Jackson creates and get the data yourself. Consider this JSON that has some data we’d like to store as two integers instead of a hash in our mode class:
To extract the two integers out of “social” and store them as integers in our class, we override how Jackson parses the “social” JSON element:
Overriding the setter is also useful if you have data you want to change at the time of parsing because sometimes an API doesn’t give it to you like you want it.
Overriding setters requires you to properly identify the JSON structure referenced by the key. For example, when we overrode how “time” was parsed above, our setTime method took a String because in our JSON, the “time” key contained a string. Up higher, when parsing “social” our setSocial method took a map because “social” contained a map. Things can get even fancier:
In this example, “performances” in the JSON is an array of hashes. Note that the parameter you specify for the argument to your overriden set method must be relevant - if “performances” actually contained a hash of hashes, this method would not be called! If you specify the type of the argument as JsonNode, you will always get called. In the above snippet if we specified the argument as type JsonNode, you would get called with an ArrayNode containing ObjectNodes that contain HashMaps. If you’re not sure what you are going to get back, start with JsonNode and work from there!
Here is an example of overriding a setter to parse an array of a custom model object that is nested within our JSON. Here is some sample JSON containing some user information:
If we want to parse the “accountIcons” array, we override how “subjects” is set and parse out the AccountIcons:
Note that for the setSubjects method we specify the argument as a JsonNode since the readValue() method requires a JsonNode. If we wanted to manually parse the data, we could change the type of subjectsJsonNode to be a Map<String, Object>. We could grab the Object with key “accountIcons,” cast it to a List<Map<String, String>>, and then iterate over and parse the items as desired.
I hope this helps anyone considering considering or having problems using Jackson for their JSON parsing. If there is something totally awesome I missed about Jackson or did something not quite right here, please feel free to send me a tweet and I’ll update this post!