Convert Java Objects to String With the Iterator Pattern

Convert Java Objects to String With the Iterator PatternThe visitor pattern often comes to mind when you need to operate on a graph of objects (like JSON, XML, or Java beans). Unfortunately, the visitor pattern uses call backs which are difficult to control from the calling code.  For example, it’s not easy to conditionally skip a branch with all its child branches and leaves from a callback.  This how-to will instead use the iterator pattern to traverse your Java object graph and create a human readable debug string.  The iterator will be general enough for you to use in other ways, as I did in building a tool to search Java objects using XPath or to record exceptions in StackHunter.

The APIs

This blog creates two separate tools for you to use: StringGenerator and ObjectIterator.

String Generator

The StringGenerator utility class converts your object graph to a string that’s easy for us humans to read.  You can use it to implement toString in your classes or just to log an object’s complete graph when debugging (regardless of how their toString methods are implemented).

The above code uses StringGenerator.generate() to convert a department, an array, and a boolean to the following formatted output.

Object Iterator

The ObjectIterator class uses the iterator patten to traverse the properties in your object (and all its children) as key-value pairs.  It treats everything the same whether they’re Java beans, collections, arrays, or maps.  ObjectIterator also takes care not to follow cycles in your object’s graph (see StringGeneratorTest.testCircularGraph() in the download).

The above code walks an object graph to produce a flat set of key-value pairs.  It uses the getValueAsString() method to bypass each object’s toString() implementation to produce a standard format.  For primitive, boxed types, strings, dates, and enums, it’s uses their original toString() implementation.  For others, it’s their class name and hashcode.

You can use the ObjectIterator.getDepth() method to add indents for easier reading (as done in the StringGenerator.generate() method).  You can also use its nextParent() method before calling next() to short circuit the current branch of the tree and skip to the next.  StringGenerator uses this to limit the number of children it outputs to 64.

Implementing the Java Object Iterator

The first step when implementing the iterator pattern is to create a common, iterator interface: IObjectIterator.  This interface will be used regardless of the actual type (Java bean, array, map, etc.) being traversed.  (Sorry if the ‘I’ prefix offends you, blame the Eclipse platform folks.)

The interface allows you to move in one direction — forward — and retrieve the current property’s name and value along the way.

Each implementation of IObjectIterator is responsible for handling traversal of one type of object.  Most take in a name prefix to use when answering their getName() call.  In the case of ArrayIterator, it tacks on the element’s index to its name: return name + "[" + nextIndex + "]"; .

How to Convert Java Objects to String Using the Iterator Pattern

Property Iterator

PropertyIterator is probably the most important iterator class.  It uses Java bean introspection to read the properties of an object to turn them into a sequence of key-value pairs.

Array Iterator

The ArrayIterator uses reflection to determine the length of the array and to retrieve each of its elements in turn.  ArrayIterator doesn’t need to worry about the details of the values returned from its getValue() method.  It’s very likely they will either be be passed to a PropertyIterator somewhere down the line.

Collection Iterator

The CollectionIterator is very similar to the ArrayIterator.  It takes an java.lang.Iterable and calls its Iterable.iterator() method to initialize its internal iterator.

Map Iterator

The MapIterator traverses the entries in a java.util.Map.  It does not actually delve into each entry’s key-value pairs, that’s the responsibility of the MapEntryIterator class.

Map Entry Iterator

The MapEntryIterator handle a single entry from a java.util.Map.  It only ever returns two things: the entry’s key, then its value.  Like the ArrayIterator and others, its results may eventually be passed to a PropertyIterator and treated as Java beans if they are complex types.

Root Iterator

The RootIterator returns a single element — the initial node.  Think of it as the root (or most outer) node in an XML document.  Its purpose is to start things off.

Putting It All Together

The ObjectIterator class (used earlier) acts as a facade, wrapping all the traversal logic together.  It determines which IObjectIterator subclass to instantiate based on the current type returned from the last getValue() call (see its iteratorFor() factory method).  It preserves the current iterator’s state on a stack when a new child iterator is created internally.  It also exposes methods like getChild() and getDepth() to provide the caller with a picture of its progress.

Implementing the String Generator

You’ve already seen how to iterate over all the properties in your object.  All that’s left is to pretty it up and add some constraints (so that you’re not creating a gigabyte-sized string).

The formatting is done on line 21.  It’s nothing fancy, just an indent appropriate to the property’s distance (or depth) from the root node.

The constraints can be seen on each of the highlighted lines:

  • line 9 – limits the generated string to about 16k characters.
  • line 13 – limits the number of children to 64 for any parent.
  • lines 21 & 25 – limits the keys and values to 64 characters each.

Conclussion

You’ve now seen how to use the iterator pattern to traverse a heterogeneous graph of objects.  The key is to delegate iteration of each type to its own class.  You also now have two tools you can modify or use as-is in your software.

About Dele Taylor

Dele Taylor is the founder of StackHunter.com -- a tool to track Java exceptions. You can follow him on Twitter, G+, and LinkedIn.

2 Responses to “Convert Java Objects to String With the Iterator Pattern”

  1. It`s cool. I usually use the JSON or Apache ToStringBuilder to override the toString() method.

  2. Thanks Nathan. I like the JSON approach too…as long as it’s formatted for reading. Cheers.