davnicwil
davnicwil

Reputation: 30967

Freemarker: access public field with no getter in template

I'm setting up views in a Dropwizard app and ran into a curious issue with Freemarker.

Following the docs here I set up a very simple example as follows

public class ExampleFreemarkerView extends View {
  private Foo foo;

  public ContractHtmlView(Foo Foo) {
    super("FooView.ftl");
    this.foo = foo;
  }

  public Contract getFoo() { return foo };
}

public class Foo {
  public String bar = "Hello World";
}

With FooView.ftl

<html>
  <body>
    <h1>${foo.bar}</h1>
  </body>
</html>

Expected output when rendering ExampleFreemarkerView is an HTML document displaying Hello World.

What actually happens is Freemarker throws an exception, complaining that ${foo.bar} - specifically bar - is undefined.

This appears to be because bar is a public field, without a getter. When I add a public String getBar() { return bar; } getter to Foo, it works.

I'm somewhat surprised that this is the case - i.e. that Freemarker seems to require getters and won't work with public fields out of the box. I'm deliberately using public fields instead of getters/setters on my model objects, so adding getters just to make Freemarker work isn't a solution I'll consider.

I've googled around a lot and read through the Freemarker docs, and just can't find any way to 'turn on' this behaviour in Freemarker. Is it possible?

Just for interest - I also tried the above example, exactly the same, but with a Mustache template and public fields work fine there (i.e. {{foo.bar}} renders Hello World without issue). That solves the immediate problem, so this question is mostly just out of curiosity or in case I decide to use Freemarker over Mustache for other reasons.


Edit based on comments - I understand that Freemarker does this (insists on getters out the box) to follow the Java Beans spec, but most libraries in the Java ecosystem support public fields - Hibernate and Jackson being prominent examples - to the extent I personally view it as an equally valid standard and find libraries not supporting it out the box surprising.

Upvotes: 4

Views: 4780

Answers (2)

ddekany
ddekany

Reputation: 31152

This all depends on the objectWrapper configuration setting. The DefaultObjectWrapper (and any BeansWrapper subclass), which most projects are using, has an exposeFields setting that can be set to true.

In Dropwizard, that can be done like this in the configuration YML, if you set up your ViewBundle in a compatible manner (based on https://github.com/apache/freemarker-online-tester):

viewRendererConfiguration:
  freemarker:  # was `.ftl:` before Dropwizard 1.3.0
    objectWrapper=DefaultObjectWrapper(2.3.28, exposeFields=true)

Upvotes: 7

Ori Marko
Ori Marko

Reputation: 58782

It's stated in freemarker docs

Every object will be wrapped into a TemplateHashModel that will expose JavaBeans properties and methods of the object. This way, you can use model.foo in the template to invoke obj.getFoo() or obj.isFoo() methods. (Note that public fields are not visible directly; you must write a getter method for them.)

Notice that it's also follow java encapsulation concept

Also you can use framework as lombok getters automatically using only class annotation

Upvotes: 1

Related Questions