Wednesday 27 February 2013

JSON, POJOs and Groovy's HTTPBuilder class


Whilst previous posts have looked at the use of RESTClient, this post will concentrate on its parent class, HTTPBuilder and also touch briefly on the JSONBuilder class. This post will cover the building and parsing of JSON, converting JSON to and from POJOs, adding authentication headers and handling response success and failures.

The HTTPBuilder class provides an API to make HTTP requests and parse the responses, and builds upon the Apache AbstractHttpClient class. The HTTPBuilder class can be instantiated with a URI to be used as default for all request methods. A default content type can also be supplied but for this example it will be the instantiated default of ContentType.ANY.

The below method takes an AccountFilter POJO, creates a JSONBuilder object and instantiates a HTTPBuilder with the default URI. It then makes a request specifying the method (POST) and contentType (JSON). The request body will be converted to a url-encoded form string. The code also shows how headers can be set, in this case, X-Application and X-Authentication using instance variables.


NOTE: The request is retrieving data and not creating or updating data, so would normally be a GET but the criteria can be complex and nested and therefore needs to be sent as JSON in the request body, hence the use of POST. (There is whole discussion on the web about GET requests containing a request body.) 


Set<Customer> listCustomers(AccountFilter accountFilter) {

    def resultList = new ArrayList<Customer>()
    def builder = new JsonBuilder(accountFilter)
    def http = new HTTPBuilder( 'https://beta-api.city81.com/json-rpc' )

 
    // POST request
    http.request (POST, JSON){req->
        body = [
            "jsonrpc" : "2.0",
            "method" : "Bank/v1.0/listCustomers",
            "params" : builder.toString(),
            "id" : 1
        ]

        headers.'X-Application' = this.applicationKey
        headers.'X-Authentication' = this.sessionToken

        // success response handler 
        response.success = { resp, json ->
            json.result.each { 
                resultList.add(new Customer(
                    it.customer.id, it.customer.name))
            }
        }

        // failure response handler 
        response.failure = { resp ->
            println "Unexpected error: ${resp.statusLine.statusCode}"
            println ${resp.statusLine.reasonPhrase}
        }
    }

    return resultList
}

The success response handler will iterate over the JSON result set and create Customer objects adding them to a collection. The failure response handler will print out the status code and reason phrase for the failure of the POST request.

As mentioned above, the code uses JSONBuilder to turn the POJO into JSON. For a Filter POJO with the field accountIds set, the JSON would appear as below:


"params":{"filter":{"accountIds":[1]}}

There is a flaw with this though in that if the filter attribute of MarketFilter is not set then the Filter tag will not be present in the JSON. If the service you are POSTing to requires this as its mandatory (albeit empty) then the req body params line would be:

"params" : [filter:[:]],

which would generate JSON:

"param": {"filter": {} }

This post covers only a small part of HTTPBuilder and one of a number of ways of POSTing a request, generating the JSON and parsing its response, many of which have been simplified with the RESTClient class.

The POJOS used in the above example:

public class Customer {

    private String id;
    private String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }
   
    // ... getters and setters
 }


 public class AccountFilter {

    private Filter filter;
  

    public AccountFilter(Filter filter) {
        this.filter = filter;
    }
   
    // ... getters and setters
 }


public class Filter {
  

    private Set<String> accountIds;

    // ... getters and setters
}