The common ways to avoid NPEs while traversing is to check each node for null before traversing it. And so you see code that looks like a ladder, eg
value = data
if ( value != null ) value = value.A
if ( value != null ) value = value.B
if ( value != null ) value = value.C
if ( value != null ) value = value.D
While this works it is very cumbersome and prone to copy-paste errors due to small variations found on some ladder rungs.
Some languages offer syntax support for traversing. This is called a null coalescing operator. Java, the language I mostly work in, does not have this operator and so a different solution is needed.
The solution I use allows for a safe traversing using data format specific classes. That is, XML has its own traverser, Json has its own traverser, etc. A fully general mechanism might be possible, but the technique is simple and so really does not warrant one. Here is the example of safely traversing some Json data
value = new Traverse(data).at('A').at('B').at('C').at('D').value()
Or, let's have B be an array and we want to traverse the first element
value = new Traverse(data).at('A').at('B').at(0).at('C').at('D').value()
The dictionary at() method is
public Traverse at( String key ) {
if ( value != null ) {
if ( value instanceof Map ) {
value = ((Map)value).get(key);
// data might be null indicating an unsuccessful,
// but safe from NPE, traversal
}
else {
// an unsuccessful traversal always sets data to null
value = null;
}
}
return this;
}
While the array at() method is
public Traverse at( int index ) {
if ( value != null ) {
if ( value instanceof List ) {
value = ((List)value).get(index);
// data might be null indicating an unsuccessful,
// but safe from NPE, traversal
}
else {
// an unsuccessful traversal always sets data to null
value = null;
}
}
return this;
}
As with the Builder pattern, the Traverse pattern always returns the traverse instance.
A common aspect is the check for type and this can be refactored out.
public Traverse at( int index ) {
List l = cast(List.class);
if ( l != null ) {
data = l.get(index);
}
return this;
}
protected <T> T cast( Class expectedClass ) {
if ( value != null && ! expectedClass.isAssignableFrom(value.getClass()) {
value = null;
}
return (T) value;
}
Expanded, working versions of XML traversal and Json traversal are at [TODO].
Oh, how do you get the traversed to value?
public <T> T value() {
return (T) value;
}
END