Thursday, March 10, 2011

Upgrade from org.json to Jackson, piece by piece, using jackson-module-org-json

1. Background

One common task for software engineers is upgrading legacy systems; things written a while ago, often by developers who have since moved on, using tools and techniques thought to be good fit back then. While the reason for upgrades are typically either addition of minor new features, or fixes to the most annoying issues, it can and should also include some pro-active work to improve quality of the system itself, to "pay off technical debt" before it is due.

One of first things you usually observe, when looking under the hood of such systems is that various design and implementation choices do not look so good any more. Sometimes technology choice turned to be a dead-end; sometimes design was based on incomplete or incorrect understand. But almost always some of supporting libraries have become obsolete: tool that was thought as best-of-breed back then may well now be amongst most despised pieces of software ever deviced.

2. Problem

One common case of "library rot" that I have often encountered is that of using "org.json" library (from http://json.org/java) for reading and writing JSON. It was the first Java JSON library around, and served as sort of proof of concept back in the early days of JSON usage. But time has not been kind to it: with arrival of more modern, fully-featured alternatives, it is the monoplane of the second world-war era; once serviceable, but now just cumbersome and slow thing that few would choose to use given a choice of modern JSON libraries like Jackson. You can get improvements in about any area, from convenience and intuitiveness to performance, just by upgrading.

When considering possible upgrate of the JSON library, the initial issues encountered are usually not very big: changing code that directly deals with reading and writing JSON is not awfully hard, and typically resulting code is much cleaner. But there is often much bigger problem outside this core JSON code: in case of org.json package, specifically, all processing is done using concrete types (JSONObject, JSONArray). And values of these types are very easy to leak outside, resulting in unintended tight coupling of non-JSON-processing code with the JSON parser implementation. While this may be a good lesson on values of proper design and encapsulation of concerns, it is of little consolation when you are looking at code that had neither.

If your only choice is to rewrite major portions of the legacy system (to mostly address relatively small JSON-specific portion), it is often easier to just let things be as they are.

3. But what if we only upgraded parser, not JSONObject/JSONArray?

Thinking about this a bit, I realized that the biggest immediate obstacle is really deep coupling via 2 value types. So what if we just continued to use these non-abstract "abstractions", but replace underlying JSON parser and generator? While this would not allow use of more convenient and proper real abstractions (of data binding to POJOs), at least it could give some nice efficiency improvements (more on this below); and make it easier to do complete conversion bit later on. Killing a beast is often easiest by thousand cuts.

Fortunately for us, Jackson 1.7 made it very easy to add support for additional data types; so I decided to spend one (and only one!) evening to write an extension module -- jackson-module-json-org available from GitHub as https://github.com/FasterXML/jackson-module-json-org -- to allow using Jackson to effectively read and write JSONObject and JSONArray types.

Usage is rather straight-forward; after registering new module with ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JsonOrgModule());

you can use all the usual conversions like so:

// read/write JSON
JSONObject ob = mapper.readValue(json, JSONObject.class); // read from a source
String json = mapper.writeValue(ob); // output as String

// convert POJOs to/from JSONObject/JSONArray
MyValue value = mapper.convertValue(jsonObject, MyValue.class);
JSONObject jsonObject = mapper.convertValue(value, JSONObject.class);

// and even conver to/from Jackson Tree model:
JsonNode root = mapper.valueToTree(jsonObject);
jsonObject = mapper.treeToValue(root, JSONObject.class);

and there you have it, convenient conversions between JSON, POJO, and org.json types.

In fact, this will also help with later stages, because you can easily (and relatively efficiently) go back and forth between representations: so we can extend "org.json-free" zone further and further away from the source. This is what I really mean by piece-by-piece approach: it is possible to do refactoring one component or area at a time.

4. But wait! There is more.... performance bonus!

By now it should be well-known that Jackson is the most performant library for doing JSON manipulation in Java. So could it be that using this module might speed up processing as well? Aren't we still using some org.json pieces? Turns out that since JSONObject is little more than a wrapper around basic Java HashMap (and JSONArray similarly around ArrayList), most performance benefits from different parser and generator. So I went ahead and modified my JSON parser performance test to compare "raw Jackson" (using Jackson Streaming API), "Jackson with Tree Model" (JsonNode), "Jackson with org.json" and stock org.json. Here is one sampling of results I saw on my machine (using sample document #5 from org.json):


Test 'Jackson, stream' -> 282 msecs
Test 'Jackson, JsonNode' -> 411 msecs
Test 'Jackson + module-org-json' -> 393 msecs
Test 'Json.org' -> 783 msecs

It turns out that while Streaming API is the fastest way, the new "jackson-module-json-org" extension can actually bind JSON as fast as Jackson Tree model -- both of which are twice as fast as basic org.json package for this small document (for bigger documents difference is typically even bigger).

So in addition to cleaning up the code, you may also end up speeding up your JSON processing code by 100%. Not a bad deal, eh?

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.