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:
- ParamsRetriever Base abstract class containing common logic.
- PositionalParamsRetriever Retrieves positional parameters (from the parameters’ JSON array).
- NamedParamsRetriever Retrieves named parameters (from the parameters’ JSON object).
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:
- The complete Java example from this article.
- The JSON-RPC 2.0 Base page
- The package JavaDocs