Photo of the Oldest Known Map
Photo courtesy of cea + with permission under license CC BY 2.0
In this post I’d like to continue exploring fundamental elements of Couchbase Mobile on Android. Much of the information will apply to iOS as well. Look for more on iOS specifically and cross-platform tools coming up.
You can read a mid-level overview of why I’m a convert to NoSQL and using Couchbase Lite instead of SQLite and other storage options for mobile here.
This post will get you up and running on Android in a couple of minutes. And this one explores basic database operations. You’ll see in that post I use Maps
extensively.
Dealing with JSON directly
You can do a lot with maps. Often you’ll pull data into other structures like lists, too. That may be all you ever need. Here’s an example of the kind of typecast needed.
1 |
List comments = (ArrayList)document.getProperty(COMMENTS_KEY); |
In the example app, I stored a set of comments as an string array. You still have to pull the array based on a key, but from there you can handle it as a list.
A touch more sophistication
For more sophisticated applications, you may want to convert your documents fully into objects.
There are several libraries around for converting JSON to/from Java Objects. The standard APIs on Android for handling JSON have some real limitations, but it is built in, so that’s an advantage. Google has a library, Gson. Gson can handle cases where you don’t have access to source. I found it interesting to read the Gson Design Document to get deeper insight into other considerations when handling JSON.
In this post I’m going to talk about another popular JSON library, Jackson. Jackson is quite mature, supports three different approaches to handling JSON, and has a strong reputation for efficiency.
Databinding
Read the details about the three modes for Jackson here if you like. Most app developer will probably want to use the databinding approach. This sounds perhaps a little intimidating, but it’s really not. Databinding here just refers to matching the JSON to properties of an object in some automated way.
For example, databinding means taking this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "id": "contact100", "type": "contact", "first_name": "John", "last_name": "Smith", "email": "john.smith@couchbase.com", "addresses": [ { "address_line": "123 Main Street", "city": "Mountain View", "country": "US" }, { "address_line": "123 Market", "city": "San Francisco", "country": "US" } ] } |
and using it to create an instance of this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Contact { public String id; public String type; public String first_name; public String last_name; public String email; public Address[] addresses; public static class Address { public String address_line; public String city; public String country; } } |
Jackson is quite good at figuring out how to do this without help. Since the fields above are all declared public, there’s nothing else to do. Assuming the JSON data is in a Map
called “map”, this snippet will fill out the a Contact
instance.
1 |
Contact contact = mapper.convertValue(map, Contact.class); |
If the JSON above represents the data in a Couchbase document, the code would be something like this.
1 |
Contact contact = mapper.convertValue(document.getProperties(), Contact.class); |
This works if the fields aren’t public if there are corresponding getters and setters.
A little help: Annotations
Finally, you can get into truly fine-grained control of your object construction using Annotations. Jackson Annotations are powerful. A complete description is beyond the scope of this blog. You can read a good guide describing them here.
POJO (Plain Old Java Objects)1
To aid in using Annotations, I found it useful to start with a base class. Here’s the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@JsonInclude(Include.NON_NULL) public class POJO { @JsonIgnore private Map<String, Object> additionalProperties = new HashMap<>(); @JsonAnyGetter public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; } @JsonAnySetter public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } |
The first annotation @JsonInclude(Include.NON_NULL)
instructs Jackson to ignore null fields (i.e. stuff missing from the data).
The next three annotations, along with the code, allow Jackson to handle any unspecified fields. Jackson will happily stuff anything you haven’t told it about into the additionalProperties map. This means you can change your data around without worrying about your code blowing up. Needless to say, this could allow real mistakes to slip unnoticed, so use with caution!
Finally, in case you want to avoid doing it yourself, here’s a link to a tool that can help automate annotating classes: http://www.jsonschema2pojo.org/
Final Note
Couchbase Lite currently uses Jackson internally. Many people would recommend not relying on this. I point this out in case you run into library conflicts or other issues.
Postscript
Check out more resources on our developer portal and follow us on Twitter @CouchbaseDev. You can post questions on our forums. And we actively participate on Stack Overflow.
You can follow me personally at @HodGreeley