Thursday, May 26, 2011

JSON vs XML for data representation in GWT

(This is Part 1 of a series. See also Part 2.)

In a few weeks, I will be working with my Morgan's Raid collaborator Ron Morris (History), Mark Groover from Anthropology, and about ten undergraduate students to develop a prototypical digital archaeology simulation. The intent of the project is to create a technological tool to teach 4th-graders about historical archaeology, that it is a scientific process and more than just a dig. I will not dwell on the project design here, but rather I would like to share some of my experimentations with JSON, XML, and GWT.

We want to have the widest possible adoption, and since the expected interactions are fairly simple, making this a Web application seemed best. GWT stands out as an excellent candidate technology since it handles cross-browser issues better than any of our team can. Flash would be a contender, of course, but I ruled it out early since I don't know enough Flash/Actionscript to be confident in leading the team in an appropriate software architecture.

We will only have five weeks to develop the prototype, and the team will include a technical team (mostly Computer Science majors and minors) and a content team (mostly Anthropology and History majors). Unlike Morgan's Raid, in which we could create content directly in Java since the designers were also developers, this project would benefit from an intermediary domain-specific language that the designers can use. An architecture I had kicked around in my head involved using Ruby to define an internal DSL that would be processed in GWT. However, it's not possible to put JRuby into a GWT project since the GWT compiler would try to transform the whole kit and kaboodle into Javascript, and that's not possible or even sensible. I'll just have to find another project in which to experiment with Ruby DSLs; for now it's off the table.

Javascript

Considering a data representation layer, I started by considering how Javascript could be used directly for configuration-based programming. That is, the technical team could write an interpreter on top of Javascript data created by the content team. I played with JSON last Summer when working with the Wave API, and it seemed like a nice way to represent domain objects. Overlay types provide a well-documented approach for using JSON to represent domain objects in GWT. As a proof of concept, I whipped up a simple message wrapper. The interface looks like this:

@SingleJsoImpl(LittleMessageImpl.class)
public interface LittleMessage {

    /**
     * Get the text of this message.
     * 
     * @return message text
     */
    public String text();
}


The annotation references another class that is shown below. Note that Google's documentation, as of this writing, does not make this relationship clear, but a little tinkering with the annotation value revealed the proper approach.


public class LittleMessageImpl extends JavaScriptObject { 

    /**
     * Create a {@link LittleMessageImpl} from JSON text.
     * @param json the JSON string
     * @return new message object
     */
    public static final native LittleMessageImpl buildMessageFromJSON(String json) /*-{
        return eval('(' + json + ')');
    }-*/;
    
    // Required for the GWT compilation process
    protected LittleMessageImpl() {}
    
    /**
     * @return the text of this message
     */
    public final native String text() /*-{
        return $wnd.checkNotNull(this.text);
    }-*/;

}

The two important bits are the factory method, which uses native Javascript to evaluate a JSON string into an object, and the Javascript native implementation of the text() method.


In debugging this program, I encountered a problem where I had called the field by two different names, and of course because Javascript is dynamically typed, I had no compiler support to detect this. To ease debugging, I wrote a nigh-trivial Javascript method, checkNotNull, that behaves like Preconditions.checkNotNull from Guava: if the argument is not null, it is returned, and if it is null, it bombs out. I put this method into a file called preconditions.js that lives in the "public" folder of my project. Making this work took some trial and error, but the crux of it is this: if your application's root package is com.example, then you can put a folder under com/example/public and put resources there, and these resources will be loaded prior to your GWT applications' execution. This is defined in the documentation, although I had a hard time extracting the previous sentence's meaning from what was given.

Coming back to LittleMessageImpl, the json itself sits right in the application: it is not the result of a request to a server, as JSON is frequently used. I made a file message.json that sits in my com.example.client package, and it is referenced as a text resource in my JsonResources class:

public interface JsonResources extends ClientBundle {
    
    static final JsonResources INSTANCE = GWT.create(JsonResources.class);
    
    @Source("message.json")
    TextResource message();

}

Now, to create my LittleMessage, I need only do this:

LittleMessageImpl message = LittleMessageImpl
                .buildMessageFromJSON(JsonResources.INSTANCE.message()
                        .getText());

Like many things, it's easy once you know how.


This was a helpful process for learning how GWT works, but I encountered a problem soon afterwards that perhaps I should have foreseen. One of the actual problems I want to solve with GWT is to have an image that has "hotspots" where, when they are moused over, something happens. This is trivially done in GWT using Image and MouseMoveHandler. The problem, however, is granularity of representation. I would like to be able to represent the image as a composite of hotspots that define geometric regions and actions. I could structure the whole thing as a JSON object, but in Java, I want it broken down into pieces, an InteractiveImage holding zero or more Hotspots, for example. This is where my knowledge of Javascript breaks down. It's not clear to me how I could take a JSON string and, with the same kind of elegance as simply evaling it, end up with a beautiful composite object. Two-pass parsing is of course an option, where the first pass makes a "dumb" Java representation that is then converted into a better domain model, but then you lose the elegance of the overlay types.


From here, I considered diving into Javascript, JSON, and GWT a bit more deeply, but I decided instead to go on a tangent. It's all experimentation, after all. Why not try that nasty old de facto standard for Web application data representation, XML?

XML

GWT has good XML support. It has to. Plus, the XML parser looks exactly like Java's XML parser that I've used a good many times. I had built some confidence from my Javascript experiments and decided to try to solve a real problem this time. My data representation in XML looks like this:

<place>
    <img src="Jellyfish.jpg" />
    <hotspot>
        <rect x1="10" x2="50" y1="10" y2="50">
        <text>You found it!</text>
    </rect></hotspot>
</place>
</code>

I happened to be on Windows when writing this, so I opened up my sample pictures folder and found a nice jellyfish image to use. (Why was I in Windows? To minimize time between experiments and Witcher 2.) Like message.json, I put this file—demo.xml—right in my com.example.client folder, and I load it as a text resource analogously.


Without overlay types, I need to do my own XML parsing. I took what I consider to be a standard approach, making a domain object called MouseablePlace that extends Composite and gave it a public static parseXML(Document) method. This then does rather mundane building of a MouseablePlace from the configuration data, but the result is a beautiful MouseablePlace which contains a series of Hotspot objects, each of which contains a Rect, and these are pulled from the XML via ad hoc recursive descent parsing. To the MouseablePlace is attached a MouseMoveHandler that checks whether the mouse is in any of the Hotspots, and if so, the Hotspot's message is shown in a status label on the screen.


Next steps

Both XML and JSON are contenders to represent the domain objects in this application. For that matter, we could also use an internal DSL in Java, but I would rather keep the learning curve as low as possible for the content team. Whether XML or JSON is more sensible to non-programmers, I have no idea. If we had nore time, we would wrap the whole thing in an editor, but we do not have that luxury.

If you have any experience or suggestions, please feel free to share them in the comments.

[ADDENDUM]
Go to Part 2, which covers my experimentation with AutoBeans.

No comments:

Post a Comment