Archive for November, 2008

XStream is a cool library for serializing Java objects to XML files, and back again. Given that I prefer to work with JavaScript, rather than Java, I spent some time today trying to make XStream work with Rhino objects.

First, a quick example of XStream, from their two minute tutorial. We start with two small Java classes:

public class Person {
  private String firstname;
  private String lastname;
  private PhoneNumber phone;
  private PhoneNumber fax;
  // ... constructors and methods
}

public class PhoneNumber {
  private int code;
  private String number;
  // ... constructors and methods
}

Set up XStream:

XStream xstream = new XStream();
xstream.alias("person", Person.class);
xstream.alias("phonenumber", PhoneNumber.class);

And instantiate the classes, plus serialize to XML:

Person joe = new Person("Joe", "Walnes");
joe.setPhone(new PhoneNumber(123, "1234-456"));
joe.setFax(new PhoneNumber(123, "9999-999"));

String xml = xstream.toXML(joe);

And here’s the result:

<person>
  <firstname>Joe</firstname>
  <lastname>Walnes</lastname>
  <phone>
    <code>123</code>
    <number>1234-456</number>
  </phone>
  <fax>
    <code>123</code>
    <number>9999-999</number>
  </fax>
</person>

Good, readable XML, as a serialization from Java objects. Going back is as easy as:

Person newJoe = (Person)xstream.fromXML(xml);

XStreaming Rhino

Unfortunately, things aren’t as easy when you try to serialize a JavaScript object from within Rhino:

xstream = new Packages.com.thoughtworks.xstream.XStream();
xstream.toXML({foo: 'bar'});

This results in a rather long error stack trace, which boils down to:

org.mozilla.javascript.WrappedException: Wrapped com.thoughtworks.xstream.converters.ConversionException: Could not call org.mozilla.javascript.NativeObject.writeObject()
---- Debugging information ----
message             : Could not call org.mozilla.javascript.NativeObject.writeObject() 
cause-message       : null 
cause-exception     : java.lang.reflect.InvocationTargetException 
-------------------------------

The problem here is that Rhino objects can’t be directly serialized by Java classes. Luckily, XStream is quite flexible and allows you to write your own serializer. Or, in XStream terms, Converter. So, that’s what I did:

xstream = new Packages.com.thoughtworks.xstream.XStream();
converter = RhinoConverter.create();
xstream.registerConverter(converter);

xstream.toXML({foo: 'bar'});

This gives:

<org.mozilla.javascript.NativeObject>
  <object>
    <foo type="string">bar</foo>
  </object>
</org.mozilla.javascript.NativeObject>

Before we continue, I’ve put the code up on Github. Here’s an example file.

If we serialize the following object:

{
  'foo': 'bar',
  'answer': 42,
  'truth': true,
  'falsehood': false,
  'nothingness': null,
  'everything': undefined,
  'nested': {
    'birds': 'nest'
  },
  'feist': [1, 2, 3, 4],
  'now': new Date
}

We get:

<org.mozilla.javascript.NativeObject>
  <object>
    <foo type="string">bar</foo>
    <answer type="number">42.0</answer>
    <truth type="boolean">true</truth>
    <falsehood type="boolean">false</falsehood>
    <nothingness type="null"/>
    <everything type="undefined"/>
    <nested>
      <object>
        <birds type="string">nest</birds>
      </object>
    </nested>
    <feist>
      <array>
        <length type="number">4</length>
        <_0 type="number">1.0</_0>
        <_1 type="number">2.0</_1>
        <_2 type="number">3.0</_2>
        <_3 type="number">4.0</_3>
      </array>
    </feist>
    <now>
      <date stamp="2008-11-09T23:13:26.282+01:00"/>
    </now>
  </object>
</org.mozilla.javascript.NativeObject> 

What’s interesting here is that only the XML root element is org.mozilla.javascript.NativeObject. This root element is created by XStream, and is a serialization of the main object. From XStream’s point of view, this main object is a Java object, as modelled by Rhino. The converter itself, however, is written in JavaScript. All descendent elements are serializations of JavaScript objects. That’s why <nested> doesn’t contain a <org.mozilla.javascript.NativeObject>.

The constructors of the JavaScript objects are retained in the XML, in the form of <object>, <array> and <date> elements. Children of these elements are the object’s properties. Primitive data types are indicated by the property having a type attribute, and a raw value. <date> elements have an extra stamp attribute, which is the value of the original Date object in RFC 3339 format. Because XML property names may not start with a digit, the array indices are prefixed by an underscore. (And yes, underscores themselves are escaped by another underscore. _ is serialized to __.)

It’s possible to add custom constructors to the converter:

function Klass() {
  this.property = 'foobar';
}

Klass.prototype.print = function() {
  print(this.property);
};

converter.register('klass', Klass);

k = new Klass;
k.property = 'hello world';

xml = xstream.toXML(k);

Here I’ve created a class Klass, and registered it with the converter we created earlier. This does run against XStream’s alias() method, which you saw earlier in the XStream example. This is because XStream deals with Java objects, and it’s our JavaScript converter that deals with JavaScript objects.

The XML serialization is as follows:

<org.mozilla.javascript.NativeObject>
  <klass>
    <property type="string">hello world</property>
  </klass>
</org.mozilla.javascript.NativeObject>

We can now deserialize the XML, and run Klass.prototype.print(), to see if deserialization worked:

k1 = xstream.fromXML(xml);
k1.print(); // 'hello world'

Marvelous!

Look ma, no constructor!

Here’s the code which creates an instance for deserialized objects:

    object = {};
    object.__proto__ = constructor.prototype;

This is where the JavaScript truly shines. Rather than doing object = new constructor(), where constructor is a previously derived reference to a constructor method, we simply create an object and point it to the prototype of the constructor, without invoking the constructor method!

At this point it may help to explain how JavaScript finds a property on an object. If we have object o, and we try to resolve o.foo, JavaScript first checks if o has a property foo. If it does, it just returns the value of this property. If it does not, JavaScript checks if the object referenced by o.__proto__ has the property. If it does, the value is returned, if it doesn’t, o.__proto__.__proto__ is checked. This continues until either foo has been found, or we run out of __proto__ references. In JavaScript, __proto__ tends to point to a prototype object of a constructor method. Object.prototype for example. The __proto__ references form a prototype chain. Object.prototype is usually the last object in this chain.

As it turns out, we could program, say, new MyClass(), ourselves, like this:

function MyClass() {
  this.foo = 'bar';
};

MyClass.prototype.baz = function() {
  return 'thud';
};

var auto = new MyClass();

auto.foo; // 'bar'
auto.baz(); // 'thud'

var manual = {};
manual.__proto__ = MyClass.prototype;
MyClass.apply(manual, []);

manual.foo; // 'bar'
manual.baz(); // 'thud'

Strings

When working with Rhino, the Java versus JavaScript object issue comes up more often. In the converter code, you’ll often see something like '' + reader.getAttribute('type'). For example:

switch ('' + reader.getAttribute('type')) {
  case 'string':
    value = '' + reader.getValue();
    break;

The problem is that reader is a Java object, and reader.getAttribute('type') returns a Java string. For some reason, Java strings don’t match JavaScript strings in switch() statements, although they often do otherwise:

javaString = new Packages.java.lang.String('string');

javaString == 'string'; // true

switch(javaString) {
  case 'string':
    print('Match');
    break;
  default:
    print('No match');
}

// 'No match'

Something to watch out for!

XStream & Rhino

Back to XStream. What I’ve built now is a basic implementation, which does not support duplicate or circular references. Neither does it serialize JavaScript functions, and I haven’t really tried serializing Java objects referenced by a JavaScript object. But, it’s a start! Let me know if you use it, find faults, or want to commit a fix.

Last month, together with Andreas Rasmussen and Anders Borre Hansen of MacVærk and Aron Allen of Copenhagen Cocoa, I co-organized iPhone Dev Camp Copenhagen. We saw fifty people show up for pizza and a quick iPhone crash course, and during the night about 20 people stayed to work on their iPhone apps.

Together with Aron, Jonathan Bunde-Pedersen and Philip Bruce I worked on TrainTrack, a neat little app which shows the next departures from S-Train stations here in Copenhagen.

UI sketch 

UI sketch

Above is a small sketch of the TrainTrack UI. Here’s what we ended up with after a night of hard work (back then, it was less broken):

Detail view 

Detail view

The feature set on the morning of October 4th:

  • Displaying next ten departures from all S-Train stations.
  • Support for favorite stations.
  • Finding the nearest station, based on GPS coordinates. Or, well, finding whether København H or Lyngby is the closest station, since we had only entered GPS coordinates for those two stations.

Since we wanted the app to work even without a data connection, and we didn’t want to query a web service to find the next departures, we had to figure out a way of getting the departure time table. The best hackish way to do that was to parse a RTF file found on the DSB website.

This then was my task. That’s right, I went to iPhone Dev Camp and I wrote Python code!

I figured out how to parse the RTF file to extract stations and departure intervals from those stations, taking into account the various exceptions, such as weekends, rush hour trains, et cetera. At least, I hope I did. I’ve had to make some improvements to the database since the camp, and I’m still not confident that it’s fully accurate. Which makes sense, because parsing an RTF file is the wrong way to get the data!

As it turns out, it should be possible to get the database from Rejseplanen, which gives us a reliable solution. Then, of course, we’ll also have to figure out how to keep the database up to date, and perhaps discover last minute delays.

For the camp though, getting the data from an RTF file worked out quite okay.

Jonathan and Philip did most of the application programming. The departure times and favorites were stored in a SQLite database. Following the MVC paradigm, we used a model to manage favorites, query train stations and departure times. There were views and controllers for the station list, favorites list, nearby stations list, and the departures list. This, of course, worked quite nicely. Unfortunately we arrived at a hacky way of figuring out the next departures, by using integers for departure times rather than NSDate objects. This introduced some bugs, especially after midnight. Another example of where the quick ‘n dirty way breaks down quickly and dirty.

That said, we really did build a prototype of the app. We’d like to take it further, make some big improvements and hopefully put it up on the App Store. The current version is working a lot better, at least:

TrainTrack, early November 

TrainTrack, early November

I had a great time at iPhone Dev Camp, and it was quite cool building a small app. It helped me grasp the concepts of Cocoa development and Cocoa itself. It’s a bit different from web programming, but still quite straightforward.

 

TrainTrack screenshots on Flickr