Alex Berg

Consuming a JSON API - Force.com vs. Java

I found myself with a few spare hours last Friday after discussing our favorite time-tracking tool with a co-worker. He was telling me how many time-tracking tools he has tried during his career as a consultant/contractor, and how his expectations have finally become satisfied after discovering Toggl, an HTML5 web app that also has mobile and OS-level apps.

While this tool works great to capture his time, we use a custom Force.com app built into our internal Salesforce instance for official recording of time. This means that an extra step is required, in which two side-by-side browser windows are used copy time from Toggl into Salesforce. After a quick check, I discovered that Toggl has an API for pulling time entries, so I let my curiosity carry me and started implementing my ideas. I’d like to walk through the design to show you the simplicity of consuming a JSON API by using the Force.com JSON class to handle the complexity by comparing it to its Java equivalent.

I don’t want to be unnecessarily verbose, so I’ll use code to help explain.

The API for getting time entries for a specific date is defined by sending an HTTP GET request to this endpoint: “https://www.toggl.com/api/v6/time_entries.json”. I can even append a start and end date to the query string to get time entries for a specific date or date range. The response is in the JSON format, and looks like this:

{ "data": [ { "duration": 900, "billable": true, "workspace": { "name": "john.doe@gmail.com's workspace", "id": 31366 }, "stop": "2010-02-12T15:51:19+02:00", "id": 2913477, "project": { "name": "Important project", "id": 189657, "client_project_name": "Important project" }, "start": "2010-02-12T15:35:47+02:00", "tag_names": [ "API" ], "description": "Todays time entry", "ignore_start_and_stop": false } ], "related_data_updated_at": "2010-06-29T11:17:19+03:00" }

Now, I have this information in Salesforce, but it is a flat String. We can’t reliably pull values from a String, so we need write a function that takes this JSON string as a parameter and returns a fully-typed Apex class, which is designed to safely store and retrieve values like this.

I discovered that a Java implementation of a wrapper for the Toggl REST API already exists, which made this a very interesting project.

Let’s take a look at the Java implementation.

public List<TimeEntry> getTimeEntries(Date startDate, Date endDate) { Client client = prepareClient(); WebResource webResource = client.resource(TIME_ENTRIES); if (startDate != null && endDate != null) { MultivaluedMap queryParams = new MultivaluedMapImpl(); queryParams.add("start_date", DateUtil.convertDateToString(startDate)); queryParams.add("end_date", DateUtil.convertDateToString(endDate)); webResource = webResource.queryParams(queryParams); } String response = webResource.get(String.class); JSONObject object = (JSONObject) JSONValue.parse(response); JSONArray data = (JSONArray) object.get(DATA); List<TimeEntry> entries = new ArrayList<TimeEntry>(); for (Object obj : data) { JSONObject entryObject = (JSONObject) obj; entries.add(new TimeEntry(entryObject.toJSONString())); } return entries; }

With this existing implementation, I had a point of reference when designing an Apex wrapper for the API. Our curiosity lies in transforming the JSON string into a fully-typed Apex class These steps are defined in the getTimeEntries method of the Java implementation, which are:
1) get the response as a String,
2) parse it into a JSONObject, which provides a ‘get’ method for retrieving a certain property, and finally
3) iterate over the JSON array and map values into a Java class.

The Java implementation is clean and seems to work well, so let’s see the Apex translation.

public List<TogglTimeEntry> getTimeEntries(DateTime startDate, DateTime endDate) { HttpRequest req = new HttpRequest(); req.setMethod('GET'); String authHeader = Toggl.buildAuthHeader(this.user, this.password); req.setHeader('Authorization', authHeader); String endpointWithQuery = Toggl.appendQuery(Toggl.TIME_ENTRIES_ENDPOINT, startDate, endDate); req.setEndpoint(endpointWithQuery); Http http = new Http(); HttpResponse res = http.send(req); String resBody = res.getBody(); //Turn the response into a Apex object. TogglTimeEntryList togglTimeEntryList = (TogglTimeEntryList)JSON.deserialize(resBody, Type.forName('TogglTimeEntryList')); return togglTimeEntryList.timeEntryList; }

In the Apex translation, we build an HTTP request that has our API key as an Authorization header, set the endpoint and query string parameters. The step we take to transform the JSON response is:
1) deserialize it straight into an Apex object by using the JSON class. The ToggleTimeEntryList is a class we wrote beforehand, which has names, types, and structure that directly map to the expected JSON format.

How much simpler can it get? This deserialize method saves us from mucking about with intermediary JSON objects and manually using loops to project it into an Apex class. Once we have the data in an Apex object, we can do easily do any number of things with it, including displaying it on a Visualforce page or mapping fields into an sObject for insertion.

The glory of the Force.com JSON class is pretty clear. There are a few other versions of the deserialize method if your project has other requirements, so check out the documentation.

Edit: As Rich Unger commented below, the Salesforce JSON class uses the open-source Jackson Java library to handle field mapping between JSON string and Java object. The jToggl implementation uses the Json-Simple library, which is quite a bit lower-level and more generic. While the comparison this article draws may be a bit unfair, we can still be happy that the Force.com platform architects chose the Jackson API to use, rather than asking its users to manually write the tedious field-mappings using a lower-level JSON parsing API. Cheers to another good decision by the Force.com platform architects!

Posted in: Salesforce, Software Development