commons-digester is one of the more useful jakarta commons libraries. Yeah, it's verbose and it might take a little while to grok if you're not used to stack processing and other xml-isms. But once grokked it's applicable many places, especially since lots of systems are passing around XML these days. Parsing a message-based document (short) into an object seems to be pretty quick too and since you're using rules it's pretty easy to extend once you get the basics down -- this article has a practical application of it.
The idea behind the digester is that you have events that fire based on element paths. A common scenario: when you hit a path create a new object, then set its attributes and child elements as properties, something like:
// Process the address from a message like: // <customer first="foo" last="bar"> // <address // street="1234 Main Street" city="Wilmerding" state="PA" /> // </customer> Digest d = new Digester(); ... // push a new object on the stack... d.addObjectCreate( "customer/address", Address.class ); // this will call on the topmost stack object // 'setStreet()', 'setCity()', 'setState()' d.addSetProperties( "customer/address" ); // ...or you can do it explicitly d.addSetProperties( "customer/address", "street", "street" ); d.addSetProperties( "customer/address", "city", "city" ); d.addSetProperties( "customer/address", "state", "state" ); // add the topmost stack object to the previous one d.addSetNext( "customer/address" );
Verbose, but not so bad. What happens when you have different types of addresses?
// <customer first="foo" last="bar"> // <address country="USA" // street="1234 Main Street" city="Wilmerding" state="PA" /> // <address country="Canada" // street="1234 Main Street" city="Toronto" province="ON" /> // </customer>
Assuming e've got a parent class 'Address' and subclasses 'CanadaAddress', 'UnitedStatesAddress', etc. How do we tell digester to create the right object? A factory, of course:
// just change the 'addObjectCreate()' to: d.addFactoryCreate( "customer/address", AddressFactory.class );
The factory class is cake:
// 'AbstractObjectCreationFactory' is from commons-digester public class AddressFactory extends AbstractObjectCreationFactory { public Object createObject( Attributes attributes ) throws Exception { String country = attributes.getValue( "country); if ( "USA".equals( country ) ) { return new UnitedStatesAddress(); } else if ( "Canada".equals( country ) ) { return new CanadaAddress(); } ...