Now, working with JavaScript objects and arrays in JavaScript is pretty easy. There's a lot of implicit coercion taking place, it's a dynamic language after all. That doesn't translate to Java very well, however. I looked at JSON-lib, FlexJSON, Jackson, and the json.org library. None of them provided the functionality I was looking for. I want to easily navigate a JSON object graph, and manipulate the values within.
Let's look at my particular use case for example. I had a JSON object for a user. Within that JSON object, I basically had the following: (Formatted for readability)
{
"services": {
"xyz": {
// XYZ specific preferences
}
}
}
Except since things are so chaotic, I don't know if the "services" node exists, or if "xyz" exists.
I'll demonstrate with Jackson here, but the process is basically the same for the other libraries I looked at.
In this example, we create the object from scratch:
ObjectNode prefs = nf.objectNode();
ObjectNode services = nf.objectNode();
prefs.put("services", services);
ObjectNode xyz = nf.objectNode();
services.put("xyz", xyz);
xyz.put("foo", "bar");
What does that same code look like in JSOG?
JSOG jsog = JSOG.createObjectNode();
jsog
.get("services")
.get("xyz")
.put("foo", "bar");
A little more straight forward, but Jackson wasn't that bad. What about a more complex example?
Instead of creating everything from scratch, what if we need to parse the existing preferences and add a value? If the JSON object doesn't contain "services", or "xyz" (which we're not sure of), we need to create those nodes.
Let's parse a string, empty in this case, and build up the same object, and add a value to "xyz" called "baz" with value 42.
Here's what you're facing with Jackson:
ObjectMapper om = new ObjectMapper();
JsonNodeFactory nf = om.getNodeFactory();
// Parse the JSON string
JsonNode json = om.readTree("");
// Get or create the prefs container object
ObjectNode prefs;
if (json.isObject()) {
prefs = (ObjectNode) json;
} else {
prefs = nf.objectNode();
json = prefs;
}
// Get or create the services object
JsonNode servicesNode = prefs.path("services");
ObjectNode services;
if (servicesNode.isObject()) {
services = (ObjectNode) servicesNode;
} else {
services = nf.objectNode();
prefs.put("services", services);
}
// Get or create the xyz object
JsonNode xyzNode = services.path("xyz");
ObjectNode xyz;
if (xyzNode.isObject()) {
xyz = (ObjectNode) xyzNode;
} else {
xyz = nf.objectNode();
prefs.put("xyz", xyz);
}
// Set the value
xyz.put("baz", 42);
And here's JSOG doing the same thing:
JSOG jsog = JSOG.parse("");
jsog
.get("services")
.get("xyz")
.put("baz", 42);
Now we're talking! JSOG pulls off in 5 clean, clear, concise lines of code what took Jackson 38 LOC to do. JSOG does the implicit object creation or coercion for you, and to make things even easier, you get builder-style syntax!
I want to point out that I'm not picking on Jackson here, this is a failing of all the libraries I looked at. I think Jackson is actually one of the easier libraries to use, which is why I chose to use it for JSOG's serialization and deserialization.
JSOG is Unlicensed. I've committed it to the public domain. I would love to see JSOG or an equivalent find it's way into each JSON library.
Visit the JSOG site, hosted on SourceForge.net and give it a whirl. I suspect people working with CouchDB will *really* appreciate JSOG.
2 comments:
Thank you for sharing this! Many good ideas, it is good to see people working to improve existing tools, try out new approaches.
@cowtowncoder
JSOG has a *lot* more features now, they're just not very well documented. I need to get on that ;)
Perhaps another blog post in the mean time..
Post a Comment