Processing JSON-RPC 2.0 request parameters

Last week I updated the JSON-RPC 2.0 Base library with a set of utility classes to assist programmers with the processing of request parameters. This updated version is numbered 1.6 (2010-02-18).

The new utility classes solve three common issues that occur when processing request parameters:

  • Provide type correct retrieval of the JSON parameter values.
  • Differentiate between mandatory and optional parameters.
  • Produce an appropriate JSON-RPC 2.0 error on a missing or invalid parameter.

These points are best illustrated with a concrete example. Consider the following RPC request signature. The parameters are specified by name, as key-value pairs packed into a JSON object (called named parameters). The other way to specify the parameters is by position, by packing the values into a JSON array (called positional parameters). The parameters are of three different types (JSON string, JSON number and JSON true/false), where the first two are mandatory and last one is optional (defaults to false).

method: "makePayment"
named parameters:
	"recipient"    : string, mandatory
	"amount"       : number, mandatory
	"confirmation" : boolean, optional (defaults to false)

Let’s now create a JSON-RPC 2.0 request for the above method. Using the JSON-RPC 2.0 Base API this is as simple as

// The requested method
String method = "makePayment";

// The request ID
int id = 0;

// The named parameters
String recipient = "Penny Adams";
double amount = 175.05;
Map params = new HashMap();
params.put("recipient", recipient);
params.put("amount", amount);

// Create a new request
JSONRPC2Request request = new JSONRPC2Request(method, params, id);

To serialise the request to a JSON string just use the toString() method

String json = request.toString();

which will produce the following output (pretty formatting mine):

{
  "method"  : "makePayment",
  "params"  : { "amount" : 175.05, "recipient" : "Penny Adams" },
  "id       : 0,
  "jsonrpc" : "2.0"
}

Then, on the server side, the received JSON string is parsed back to a request object, which is then dispatched to the appropriate handler depending on the requested method name.

try {   
        request = JSONRPC2Request.parse(json);
} catch (JSONRPC2ParseException e) {
        // handle parse error ...
}

String method = request.getMethod();
// ... dispatch request to appropriate handler based on method name ...

Within the handler we can now proceed with extracting the request parameters. The utility package provides three Java classes for that:

Since the makePayment method expects named parameters (key-value map) we need to create a new NamedParamsRetriever.

// Check if we got named/JSON object params
if (request.getParamsType() != JSONRPC2ParamsType.OBJECT) {
        // Report params type error...
}
Map params = (Map)request.getParams();
NamedParamsRetriever np = new NamedParamsRetriever(params);

Now we proceed by retrieving the request parameters according to their type.

The retriever methods for mandatory parameters have the form

[return-type] getXXX(String name)

where XXX is the expected parameter type and name specifies the parameter, e.g.

boolean  getBoolean(String name)
int      getInt(String name)
float    getFloat(String name)
String   getString(String name)
...
String[] getStringArray(String name)
...

These methods will throw an JSONRPC2Error.INVALID_PARAMS if the parameter is missing or its type doesn’t match the expected. You can use this ready error object to create the appropriate JSONRPC2Response straightaway.

For optional parameters, such as “confirmation” in our example, we use methods that will return the specified default value if it isn’t set in the received request. They have the form

[return-type] getOptXXX(String name, [return-type] defaultValue)

where XXX is the expected parameter type, name specifies the parameter and the last argument the default value, e.g.

boolean  getOptBoolean(String name, boolean defaultValue)
int      getOptInt(String name, int defaultValue)
float    getOptFloat(String name, float defaultValue)
String   getOptString(String name, String defaultValue)
...
String[] getOptStringArray(String name, String[] defaultValue)
...

These methods will throw an JSONRPC2Error.INVALID_PARAMS only if the parameter’s type doesn’t match the expected.

The PositionalParamsRetriever class has similar methods, except that they accept an integer argument specifying the parameter’s position.

So, let’s proceed with the actual code now.

// Extract parameters
try {   
        // Extract mandatory "recipient" parameter
        String recipient = np.getString("recipient");

        // Extract mandatory "amount" parameter
        double amount = np.getDouble("amount");

        // Extract optional "confirmation" parameters, defaults to false
        // if undefined
        boolean confirmation = np.getOptBoolean("confirmation", false);
} catch (JSONRPC2Error err) {
         // If a mandatory parameter is missing or there is a type mismatch
         // you get a ready JSON-RPC 2.0 error object which you can use
         // to construct the appropriate response
         return new JSONRPC2Response(err, id);
}

Note that if a retrieval method fails, it will throw a ready JSONRPC2Error object which you can use to form the server’s response.

Look at the JavaDocs to see a list of all types the retriever classes support. They include all primitive types such as boolean, int and float as well as more complex types such as enumerated strings, key-value maps and arrays of primitives and strings.

Links: