JSON-RPC 2.0 Base
- Minimalist Java library for composing and
parsing JSON-RPC 2.0 messages - No framework or transport layer
dependencies - Batching left out for simplicity
1. Practial and efficient Java classes for JSON-RPC 2.0 messages
This package concentrates on providing the most simple and practical Java representation of the JSON-RPC 2.0 protocol messages. Use it as a starting point to furnish a JSON-RPC 2.0 interface to your web application or service. Kept minimal and efficient, it doesn't impose any particular framework or transport onto you. You get a base set of classes to parse, represent and serialise JSON-RPC 2.0 messages and handle the respective exceptions. In just 33K of JAR-compressed byte code.
2. What is JSON-RPC and what is it good for?
JSON-RPC is a protocol of the web. While it can be used with just about any network transport to carry the messages, its JSON encoding and relative simplicity (inspired by XML-RPC) make it ideal for accessing services from JavaScript, mobile and web applications.
3. Version 2.0 of the JSON-RPC protocol
This package implements version 2.0 of JSON-RPC. Explaining the protocol is beyond the scope of this writing, however, these are the two key improvements from the original 1.0 version:
- Introduces named parameters.
Version 1.0 supports only positional (list) parameters to be passed with the
call, e.g.
["arg1", "arg2", ...]
. Version 2.0 also allows you to pack your parameters as a JSON object, e.g.{"key1":"val1", "key2":"val2",...}
. This is extremely useful if you have calls with optional parameters. You can also add new parameters in future without breaking backward API compatibility. - Standardises error reporting and codes. Version 2.0 specifies the exact format of returned request errors and introduces a set of standard error codes.
Here is an example version 2.0 JSON-RPC request (with pretty formatting applied):
{ "method" : "accounts.addUser", "params" : { "name" : "Jason Remote", "age" : 25, "email" : "[email protected]" }, "id" : 123, "jsonrpc" : "2.0" }
And here is an example response:
{ "result" : "uid-001", "id" : 123, "jsonrpc":"2.0" }
The JSON-RPC 2.0 specification also allows sending of batch (multicall) requests. The rationale for this feature is to improve RPC performance over HTTP connections. However, judging by user posts in the JSON-RPC forum, this tends to confuse people and so it was deliberately left out in this implementation.
The original JSON-RPC website is at json-rpc.org. Discussions and more recent standardisation work are at groups.google.com/group/json-rpc.
4. Requirements
- Java 1.6 (or newer) runtime
- JSON Smart takes care of JSON encoding and decoding. Its JAR is included in the distributed JSON-RPC 2.0 ZIP package for convenience.
- JUnit if you wish to run or extend the unit tests.
Download the package jsonrpc2-base-{version}.jar
JAR file and its
JSON Smart JAR dependency to your computer, then place them in your
CLASSPATH
and you're ready to go.
If you use Maven to build your project:
<dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>jsonrpc2-base</artifactId> <version>{version}</version> </dependency>
where {version} should be the latest stable.
5. The life cycle of a JSON-RPC 2.0 request
The table below shows the life cycle of a JSON-RPC 2.0 request and the Java classes + methods typically used at each step:
Step | Side | Action | Used methods |
---|---|---|---|
1. | Client | Create a new request | JSONRPC2Request() |
2. | Client | Serialise request to string and send | JSONRPC2Request.toString() |
3. | Server | Parse received string back to request object | JSONRPC2Request.parse() |
4. | Server | Get the request data | JSONRPC2Request.getMethod() JSONRPC2Request.getParamsType() JSONRPC2Request.getPositionalParams() JSONRPC2Request.getNamedParams() JSONRPC2Request.getID()
|
5. | Server | Create a response | JSONRPC2Response() |
6. | Server | Serialise response to string and send back | JSONRPC2Response.toString() |
7. | Client | Parse received string back to response object | JSONRPC2Response.parse() |
8. | Client | Check the response for success, get the result/error | JSONRPC2Response.indicatesSuccess() JSONRPC2Response.getResult() JSONRPC2Response.getError()
|
6. JSON ↔ Java entity mapping
The JSON-RPC 2.0 Base library applies the following mapping between JSON and Java entities, which you must bear in mind when composing or extracting request parameters. This mapping is actually defined by the underlying JSON Smart package which is used for JSON encoding and decoding.
JSON | Java |
---|---|
true|false | java.lang.Boolean |
number | java.lang.Number |
string | java.lang.String |
array | java.util.List |
object | java.util.Map |
null | null |
For more information check out the JSON Smart wiki. JSON Smart is a fork of the popular but stale JSON.simple toolkit, offering vastly improved parse performance and other goodies.
7. Example use
Let's now go through a JSON-RPC 2.0 example where the client sends a request to the server to make a payment on the user's behalf. Upon success the server returns a response containing a string with the payment transaction ID.
First we need to import the necessary packages.
// The JSON-RPC 2.0 classes import com.thetransactioncompany.jsonrpc2.*; // We'll need the standard Map and HashMap classes too import java.util.*;
On the client side we create a request object, which we then serialise to a string to be sent out to the server using whatever transport mechanism is required (usually HTTP):
// The remote method to call String method = "makePayment"; // The required named parameters to pass Map<String,Object> params = new HashMap<String,Object>(); params.put("recipient", "Penny Adams"); params.put("amount", 175.05); // The mandatory request ID String id = "req-001"; // Create a new JSON-RPC 2.0 request JSONRPC2Request reqOut = new JSONRPC2Request(method, params, id); // Serialise the request to a JSON-encoded string String jsonString = reqOut.toString(); // jsonString can now be dispatched to the server...
Here is the resulting JSON-encoded request string (pretty formatting applied extra):
{ "id" : "req-001", "method" : "makePayment", "params" : { "amount" : 175.05, "recipient" : "Penny Adams" }, "jsonrpc" : "2.0" }
On the server side, after receiving request string we proceed like this:
// Parse request string JSONRPC2Request reqIn = null; try { reqIn = JSONRPC2Request.parse(jsonString); } catch (JSONRPC2ParseException e) { System.out.println(e.getMessage()); // Handle exception... } // How to extract the request data System.out.println("Parsed request with properties :"); System.out.println("\tmethod : " + reqIn.getMethod()); System.out.println("\tparameters : " + reqIn.getNamedParams()); System.out.println("\tid : " + reqIn.getID() + "\n\n"); // Process request... // Create the response (note that the JSON-RPC ID must be echoed back) String result = "payment-id-001"; JSONRPC2Response respOut = new JSONRPC2Response(result, reqIn.getID()); // Serialise response to JSON-encoded string jsonString = respOut.toString(); // The response string can now be sent back to the client...
The response string:
{ "id" : "req-001", "result" : "payment-id-001", "jsonrpc" : "2.0" }
Back on the client side we parse the response and check whether it was successful or an error was reported:
// Parse response string JSONRPC2Response respIn = null; try { respIn = JSONRPC2Response.parse(jsonString); } catch (JSONRPC2ParseException e) { System.out.println(e.getMessage()); // Handle exception... } // Check for success or error if (respIn.indicatesSuccess()) { System.out.println("The request succeeded :"); System.out.println("\tresult : " + respIn.getResult()); System.out.println("\tid : " + respIn.getID()); } else { System.out.println("The request failed :"); JSONRPC2Error err = respIn.getError(); System.out.println("\terror.code : " + err.getCode()); System.out.println("\terror.message : " + err.getMessage()); System.out.println("\terror.data : " + err.getData()); }
You can download the complete example from here.
8. Generic parsing of JSON-RPC 2.0 messages
The above example assumed that the server is expecting only JSON-RPC requests. However, the protocol also defines another type of messages - notifications. They can be used in the following situation:
- The remote-procedure call doesn't return a result
- and the message is not important (notifications are unreliable per JSON-RPC definition - the client gets no feedback upon reception and processing)
To parse JSON-RPC 2.0 messages of all types - requests, notifications
and responses - use the static
parse()
method of the base abstract class
JSONRPC2Message
.
The classes JSONRPC2Request, JSONRPC2Notification and JSONRPC2Response
inherit from it.
Here is an example illustrating how you could do this:
JSONRPC2Message msg = null; try { msg = JSONRPC2Message.parse(jsonString); } catch (JSONRPC2ParseException e) { System.out.println(e); // handle exception } if (msg instanceof JSONRPC2Request) { System.out.println("The message is a Request"); } else if (msg instanceof JSONRPC2Notification) { System.out.println("The message is a Notification"); } else if (msg instanceof JSONRPC2Response) { System.out.println("The message is a Response"); }
The complete example demonstrating combined parsing of JSON-RPC messages can be downloaded from here.
9. Utility classes to extract positional and named parameters
In addition to the base classes offered here, you also get two utility classes to ease the retrieval of the paramaters once the request is received and correctly identified (by its method name):
- PositionalParamsRetriever Provides a set of getter methods for extracting request parameters specified by position (as a JSON array)
- NamedParamsRetriever Provides a set of getter methods for extracting request parameters specified by name (as a JSON object)
There are getter methods that correspond to each JSON type, which are then split into two variants - one for retrieving mandatory parameters (will raise an exception if the parameter is missing) and another one for retrieving optional parameters (allows a default value to be specified if the parameter wasn't set).
Let's illustrate their usage with an example.
Suppose you have a method "makePayment" which takes three parameters specified by name. Its signature looks like this:
method: "makePayment" named parameters: "recipient" : string, mandatory "amount" : number, mandatory "confirmation" : boolean, optional (defaults to false)
To extract the parameter values we need to create a new
NamedParamsRetriever
instance from the parsed
request.
First, import the class from the *.util
package:
import com.thetransactioncompany.jsonrpc2.util.*;
Then
// Parse the request JSONRPC2Request request = null; try { // suppose "json" contains the request string JSON... request = JSONRPC2Request.parse(json); } catch (JSONRPC2ParseException e) { // handle parse exception... } // Get the requested method... String method = request.getMethod(); // ...after choosing the appropriate handler for this method... // We expect named parameters (JSON object) JSONRPC2ParamsType paramsType = request.getParamsType(); if (paramsType != JSONRPC2ParamsType.OBJECT) { // raise exception... } // Create named parameters instance from params Map Map<String,Object> params = request.getNamedParams(); NamedParamsRetriever np = new NamedParamsRetriever(params);
Now we are ready to retrieve the method parameters.
To retrieve mandatory parameters we must call a
getXXX(String name)
method where XXX
is the
expected value type. E.g. getDouble("amount")
to retrieve
a double value specified by the name "amount". The method will throw a
JSONRPC2Error.INVALID_PARAMS
exception if the parameter is missing.
To retrieve an optional parameter we must call a
getOptXXX(String name, Object defaultValue)
method
where XXX
is the expected value type. Use the
second argument to specify a default value if the parameter
wasn't set. E.g. getOptBoolean("confirmation", false)
to retrieve an optional boolean parameter by the name "confirmation"
with a default value of false
.
// 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 e) { // If a mandatory parameter is missing or there is a type mismatch // you get an exception }
The complete Java example can be downloaded from here.
10. JavaDoc documentation
The JSON-RPC 2.0 Base source comes with extensive documentation in the form of JavaDocs. A copy of the docs is available for browsing online.
11. Download
The JSON-RPC 2.0 Base package is offered under the Apache 2.0 open source license.
JSON-RPC 2.0 BaseChanges to the source code are tracked in the Git repo at https://bitbucket.org/thetransactioncompany/json-rpc-2.0-base.
Get in touch with me if you have questions or comments about the JSON-RPC 2.0 Base software. For general questions go to the JSON-RPC forum.
You may also want to have a look at the companion JSON-RPC 2.0 Shell product, a must have tool for developers undertaking serious JSON-RPC 2.0 work.
12. Change log
- version 1.0 (2009-10-04)
- First release.
- version 1.1 (2009-12-04)
- Official release.
- Major refactoring.
- version 1.2 (2009-12-27)
- More detailed messages in parser exceptions.
- version 1.3 (2010-01-06)
- Adds convenient constant instances of the JSONRPC2Error class for all standard JSON-RPC 2.0 errors (with proper codes and messages): PARSE_ERROR, INVALID_REQUEST, METHOD_NOT_FOUND, INVALID_PARAMS, INTERNAL_ERROR.
- version 1.4 (2010-02-02)
- New JSONRPC2ParamsType class enumerates the three possible JSON-RPC parameter types: NO_PARAMS, ARRAY and OBJECT.
- Improves the documentation.
- version 1.5 (2010-02-12)
- Adds the utility classes PositionalParamsRetriever and NamedParamsRetriever to ease the processing of request parameters (type and mandatory/optional parameter checking, default values).
- Introduces JUnit test cases.
- version 1.6 (2010-02-18)
- Adds a base absract class ParamsRetriever to the utility parameter retriever classes PositionalParamsRetriever and NamedParamsRetriever.
- Adds methods for getting enumerated strings to the parameter retriever classes.
- version 1.7 (2010-02-24)
- Adds a getNames() method to NamedParamsRetriever to list the names of all parameters.
- Adds the checking methods checkNames(String[] mandatoryNames) and checkNames(String[] mandatoryNames,String[]optionalNames) to NamedParamsRetriever which will throw an exception if a mandatory parameter name is missing or a name outside the expected is present.
- version 1.8 (2010-03-11)
- Changes the signature of PositionalParamsRetriever.getOptEnumString()
- Changes the signature of NamedParamsRetriever.getOptEnumString()
- version 1.9 (2010-03-12)
- Refactors the PositionalParamsRetriever class, hasParameter() and ensureParameter() methods.
- Refactors the NamedParamsRetriever class, hasParameter() and ensureParameter() methods.
- version 1.9.1 (2010-03-15)
- Minor internal refactoring in the util classes.
- version 1.9.2 (2010-08-05)
- Util parameter retriever methods getEnumString() add support for Java enum constants.
- version 1.9.3 (2010-08-09)
- Adds explicit type parameter to util retriever methods getOpt().
- version 1.9.4 (2010-08-19)
- Renames util PositionalParamsRetriever.getEnum()|getOptEnum() and NamedParamsRetriever.getEnum()|getOptEnum() methods.
- version 1.10 (2010-09-07)
- Adds util retriever methods to allow optional null values for JSON string, JSON array and JSON object parameters. The generic ensure(), get() and getOpt() methods are also updated for this.
- version 1.11 (2010-10-04)
- Factors out common parse logic to the JSONRPC2Message class.
- Parse exceptions provide specific message on empty JSON string.
- version 1.12 (2010-12-12)
- Released under the Apache Open Source License 2.0.
- version 1.13 (2011-03-08)
- Minor changes to the Ant build.xml script.
- Includes version number in the JAR file.
- version 1.14 (2011-03-09)
- Allows optional parsing of JSON objects where their original member order is preserved.
- Allows for non-strict parsing of JSON-RPC 2.0 messages where the "jsonrpc" version field is not checked.
- Fixes JSONArray cast bug in JSONRPC2Message.parseJSONObject().
- Documentation improvements.
- version 1.15 (2011-05-19)
- Allows for concurrent execution of the static JSONRPC2Message.parse method.
- Includes field in JSONRPCParseException to indicate the cause as bad JSON or protocol exception.
- version 1.16 (2011-05-19)
- Refactors parsing, adds a reusable JSONRPC2Parser class.
- version 1.17 (2011-06-11)
- Switches the underlying JSON library from JSON.simple to the interface compatible JSON Smart 1.0.6.4 for faster and more efficient parsing.
- Expands use of Java generics.
- version 1.18 (2011-07-07)
- Completes the introduction of Java generics into the utility retriever classes.
- version 1.19 (2011-07-10)
- Renames parser methods "noStrict" to "ignoreversion" and "isNoStrict" to "ignoresversion".
- Adds methods for appending non-standard attributes (as aditional JSON object members) to JSON-RPC 2.0 messages.
- Additional parser constructors and static parse methods to allow setting of non-standard attribute parsing.
- version 1.20 (2011-07-13)
- Re-introduce parser methods "noStrict" and "isNoStrict" as deprecated.
- Documentation improvements.
- Updates JSON Smart JAR to 1.0.9.
- version 1.21 (2011-08-05)
- Provides an equals method implementation to the JSONRPC2Error class which compares the error code values.
- version 1.21.1 (2011-08-06)
- Replaces String.isEmpty() by String.length()==0 for Google Android compatibility (pre 9 level APIs).
- version 1.22 (2011-10-08)
- Utility parameter retriever classes relax the requirement of the underlying datatype for int, long, float and double values by onverting from the more general java.lang.Number representation.
- Removes the get{Opt}[Type]Array(...) methods from the utility parameter retriever classes.
- version 1.23 (2011-10-09)
- Reintroduces the previously removed utility parameter retriever methods get{Opt}StringArray(...).
- version 1.24 (2011-10-25)
- Adds methods hasParameters(String[] names) and hasParameters(String[] mandatoryNames, String[] optionalNames) to the utility NamedParamsRetriever class.
- Updates JSON Smart JAR to 1.0.9-1 (fixes parse bug #15).
- version 1.25 (2012-02-19)
- Updates JSON Smart JAR to 1.1.
- version 1.25.1 (2012-04-03)
- Updates JSON Smart JAR to 1.1.1.
- version 1.26 (2012-07-08)
- Adds class cast exception checking to critical sections in the utility retriever classes.
- version 1.27 (2012-07-14)
- Deprecates JSONRPC2Message.toJSON() in favour of JSONRPC2Message.toJSONObject().
- JSONRPC2Message implements net.minidev.json.JSONAware.
- Updates build.xml script for automatic version number substitution and ZIP file generation.
- Updates JUnit JAR to 4.10.
- version 1.28 (2012-11-11)
- Adds getParams() method to {Positional|Named}ParamsRetriever.
- Renames methods for consistent use of "std" and "params" abbreviation, old ones deprecated.
- Adds static JSONRPC2Error appendMessage and setData methods.
- Adds standard annotations.
- version 1.29 (2012-11-28)
- Converts JSONRPC2ParamsType to enum type.
- {Positional|Named}ParamsRetriever detail cause on JSON-RPC 2.0 "Invalid parameters" (-32602) error.
- version 1.30 (2012-11-30)
- Improves type safety of JSON-RPC 2.0 request and notification parameters by introducing dedicated setter and getter methods for positional and named parameters.
- Removes deprecated JSONRPC2Message.toJSON(), JSONRPC2Parser.noStrict() and JSONRPC2Parser.isNoStrict() methods.
- Adds type check for error objects during JSON-RPC 2.0 response parsing.
- version 1.31 (2012-12-23)
- Refactors retrieval of positional and named parameters.
- version 1.32 (2013-02-19)
- Corrects null argument treatment in JSONRPC2Parser.
- version 1.32 (2013-02-19)
- Corrects null argument treatment in JSONRPC2Parser.
- version 1.33 (2013-03-29)
- Switches project build from Ant to Maven.
- version 1.34 (2013-03-29)
- Updates Maven assembly plugin configuration.
- version 1.34.1 (2013-03-30)
- Updates Maven pom.xml.
- version 1.34.2 (2013-03-30)
- Fixes bug in assembly-dist.xml.
- version 1.34.3 (2013-04-02)
- Publishes library to Maven Central.
- version 1.34.4 (2013-04-02)
- Removes JavaDocs @version tags.
- Simplifies pom.xml build.
- version 1.35 (2013-04-07)
- Refactors JSONRPC2Error class.
- version 1.36 (2015-03-16)
- Cleans up code.
- Upgrades to JSON Smart 1.3.1.
- version 1.37 (2015-03-26)
- Identifiers fall back to toString() if not an instance of Boolean, Number or String (iss #4).
- version 1.38 (2015-03-27)
- JSON Smart assumes the entire JSON serialisation of JSON-RPC 2.0 response results.