Search code examples
javajsoupoption-type

Using Jsoup Elements.select() with Optional.ifPresent


Forgive me for the beginner question. I'm trying to build a web scraper . I made a helper class Selectors to store CSS selectors of different type and the class has a getter method that returns a optional field value:

public Optional<String> getName() {
    return Optional.ofNullable(this.name);
}

In my main Scraper class I have a method to extract the name from the HTML using the Selectors object like this:

public String extractName(Element building) {
    if (this.selectors.getName().isPresent()) {
        String cssSelector = this.selectors.getName().get();
        Elements buildingNameElement = building.select(cssSelector);
        return buildingNameElement.text();
    }
    return "N/A";
}

As much as I've read this isn't a very nice way to use the Optional class. But I'm struggling to come up with a better solution. My first thought was to use the ifPresent method but it does not work in this manner:

public String extractName(Element building) {
    this.selectors.getName().ifPresent(() -> {
        String cssSelector = this.selectors.getName().get();
        Elements buildingNameElement = building.select(cssSelector);
        return buildingNameElement.text();
    });
    return "N/A";
}    

I'd like that the Elements.select() would execute only if there's a name field present in the Selectors object. Could anyone help me make the code a bit more functional?

Thanks!


Solution

  • public String extractName(Element building) {
         return this.selectors
                 .getName()
                 .map(cssSelector -> {
                            Elements buildingNameElement = building.select(cssSelector);
                            return buildingNameElement.text();
                        })
                 .orElse("N/A");
    }  
    

    This is what Optional.map is for. When you do return inside a lambda, you are only returning from the lambda, not from the outer method. So the above uses the text of the building name element if getName returned a name/selector. And returns N/A if not.

    If you’re fine with a more condensed syntax and fewer named variables, you may go with the following:

         return this.selectors
                 .getName()
                 .map(cssSelector -> building.select(cssSelector).text())
                 .orElse("N/A");
    

    Disclaimer: I haven’t got JSoup on my computer, so I haven’t tested. Please forgive if there’s a typo, and report back.