Reputation: 30967
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
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
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