3

I have this path for a MongoDB field main.inner.leaf and every field couldn't be present.

In Java I should write, avoiding null:

String leaf = "";
if (document.get("main") != null && 
        document.get("main", Document.class).get("inner") != null) {
    leaf = document.get("main", Document.class)
        .get("inner", Document.class).getString("leaf");
}

In this simple example I set only 3 levels: main, inner and leaf but my documents are deeper.

So is there a way avoiding me writing all these null checks?

Like this:

String leaf = document.getString("main.inner.leaf", "");
// "" is the deafult value if one of the levels doesn't exist

Or using a third party library:

String leaf = DocumentUtils.getNullCheck("main.inner.leaf", "", document);

Many thanks.

1 Answer 1

3

Since the intermediate attributes are optional you really have to access the leaf value in a null safe manner.

You could do this yourself using an approach like ...

if (document.containsKey("main")) {
    Document _main  = document.get("main", Document.class);
    if (_main.containsKey("inner")) {
        Document _inner = _main.get("inner", Document.class);
        if (_inner.containsKey("leaf")) {
            leafValue = _inner.getString("leaf");
        }
    }
}

Note: this could be wrapped up in a utility to make it more user friendly.

Or use a thirdparty library such as Commons BeanUtils.

But, you cannot avoid null safe checks since the document structure is such that the intermediate levels might be null. All you can do is to ease the burden of handling the null safety.

Here's an example test case showing both approaches:

@Test
public void readNestedDocumentsWithNullSafety() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    Document inner = new Document("leaf", "leafValue");
    Document main = new Document("inner", inner);
    Document fullyPopulatedDoc = new Document("main", main);

    assertThat(extractLeafValueManually(fullyPopulatedDoc), is("leafValue"));
    assertThat(extractLeafValueUsingThirdPartyLibrary(fullyPopulatedDoc, "main.inner.leaf", ""), is("leafValue"));


    Document emptyPopulatedDoc = new Document();
    assertThat(extractLeafValueManually(emptyPopulatedDoc), is(""));
    assertThat(extractLeafValueUsingThirdPartyLibrary(emptyPopulatedDoc, "main.inner.leaf", ""), is(""));

    Document emptyInner = new Document();
    Document partiallyPopulatedMain = new Document("inner", emptyInner);
    Document partiallyPopulatedDoc = new Document("main", partiallyPopulatedMain);

    assertThat(extractLeafValueManually(partiallyPopulatedDoc), is(""));
    assertThat(extractLeafValueUsingThirdPartyLibrary(partiallyPopulatedDoc, "main.inner.leaf", ""), is(""));
}

private String extractLeafValueUsingThirdPartyLibrary(Document document, String path, String defaultValue) {
    try {
        Object value = PropertyUtils.getNestedProperty(document, path);
        return value == null ? defaultValue : value.toString();
    } catch (Exception ex) {
        return defaultValue;
    }
}

private String extractLeafValueManually(Document document) {
    Document inner = getOrDefault(getOrDefault(document, "main"), "inner");
    return inner.get("leaf", "");
}

private Document getOrDefault(Document document, String key) {
    if (document.containsKey(key)) {
        return document.get(key, Document.class);
    } else {
        return new Document();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I want avoid writing null checks, I know that them are necessary. The question I've posted resumes it: is there a third library that implements for me the null checks?
My answer provides two approaches: write your own null checks or use a third party library. Have a look at the method: extractLeafValueUsingThirdPartyLibrary which uses PropertyUtils.getNestedProperty() from Commons BeanUtils.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.