Saturday, October 30, 2010

From homegrown JSON protocol to JSON-RPC

As I discussed in the previous post, JSON now starts to become fashionable, mostly because of its more compact encoding compared to XML. If we are commited to communicate over JSON with the server, the need for the most common client-server communication pattern, Remote Procedure Call (RPC) arise.

JSON has such a solution, called JSON-RPC. JSON-RPC is a lightweight client-server protocol. Beside the usual stuff (remote method identification, parameter and result encoding, exceptions) it has some remarkable properties that make it particularly suitable for mobile communication.

There are two JSON-RPC specifications out there, JSON-RPC 1.0 and 2.0. While 2.0 in most aspects can be seen as a natural extension of 1.0, there are differences too. JSON-RPC 1.0 was designed to be peer-to-peer with the main transport protocol being TCP. HTTP was also supported but with limitations. JSON-RPC 2.0 is explicitly client-server even though the change is only in the terminology used in the specification. The client-server nature of JSON 2.0 ensures, however, that HTTP as a transport is more naturally supported.

Let's start with the basics.

This is a JSON 1.0 request:

{"method":"add","params":[3,4],"id":0}

"method" attribute specifies the remote method, "params" attribute has a value of an array with the RPC arguments. The order matters and has to be the same as the arguments on the remote side. JSON-RPC requests and responses are connected with the "id" attribute because requests and responses does not have to be ordered. The response following a request - or over a full duplex bearer like TCP arriving at any time - does not have to be the response to the request just sent. It may be a response to an earlier request and the "id" parameter guarantees that requests can be coupled with their responses.

This is a JSON 1.0 response:

{"error": null, "result": 5, "id": 0}

"result" is the return value of the RPC call, "id" is the same as the corresponding request and "error" carries the error object if there was an error during the RPC processing. RPC 1.0 does not define the error value beside stating that it is an object.

JSON-RPC 2.0 extends JSON-RPC 1.0 in many important ways.
  • In order to provide backward compatibility, JSON-RPC 2.0 requests and responses must all have "jsonrpc": "2.0" attribute.
  • Error object is now specified. It has "code", "message" and "data" attributes (more about that in the specification).
  • It specifies oneway messages that JSON-RPC 2.0 calls notifications. Notifications are requests without "id" attribute. No response can be sent to notifications.
  • There are batched requests and responses. Batch is formed by having an array as top-level element and adding JSON-RPC request or response objects tothis array. E.g. the following is a batched request:
[{"method":"add","params":[2,3],"id":0,"jsonrpc":"2.0"},
{"method":"mul","params":[4,5],"id":1,"jsonrpc":"2.0"}]


and the following is a batched response:

[{"error": null, "jsonrpc": "2.0", "result": 5, "id": 0},
{"error": null, "jsonrpc": "2.0", "result": 20, "id": 1}]


The following is a JSON-RPC 2.0 error response:

{"jsonrpc": "2.0","error": {"message": "ServiceRequestNotTranslatable", "code": 1000}, "result": null, "id": 12}

That was a lot of explanations, let's see something that works!

Click here to download the example program.

This is the same client-server batch calculator that you have seen in the previous post. Use the deployment instructions of the previous post to try out the example program. If you want to try the example on a real phone, I recommend deploying the server part on the real Google App Engine infrastructure as described in the previous post.

The example program uses JSON-RPC 2.0 as communication protocol between the client and server instead of our homegrown protocol. The biggest change is on the server side, which is implemented as a Python application for Google App Engine. Instead of obscure parsing code, you see remote methods defined like this:

@ServiceMethod
def add(self, v1,v2):
return v1+v2

and the rest is done by the server-side Python JSON-RPC 2.0 framework which I hacked out of this JSON-RPC 1.0 implementation. The client side does not use any framework at all which is a major problem with this example program. So far I have not been able to find any decent JSON-RPC 2.0 client-side framework suitable for Android. If you have an idea, please comment!

6 comments:

Tomas said...

Haven't tried it out yet, but maybe this one?

http://code.google.com/p/android-json-rpc/

Gabor Paller said...

Thanks, Tomas, I will try it out and share the experiences. It will take some time, however. :-)

grnadav said...

pardon my french,
but why not try JSON with REST?
why re-invent the wheel with a whole new vocabulary (RPC) instead of using CRUD with REST?
what am i missing here?

Gabor Paller said...

grnadav, the problem goes back to the well-documented verbosity of XML. XML encoding is not bandwidth-efficient, that's why it is not used e.g. in telecom signalling.

XML's verbosity can be tolerated when bandwidth and processing power is abundant, e.g. in traditional enterprise IT applications. One can argue that this is the case with 3G data connection. Maybe yes but the fact is that 40% saving in bandwidth is still relevant in wireless communication.

There are other solutions, e.g. binary XML. Somehow widespread deployment of binary XML outside of WAP world did not progress as quickly as it could have. So there is this attractive replacement which is almost as good as XML but consumes much less bandwidth.

andry said...

very good tutorial and can hopefully help me in building json in the application that I created for this lecture. thank you

Unknown said...

thanks for this article, it was very helpful.it help to understand JSON-RPC,
2 day and i hadn't advanced