The main reason is that unlike in the Java language, in the template language there's no separate namespace for methods, and collection elements. There's actually a setting in BeansWrapper to expose both, but because of the common namespace that leads to some ambiguities and surprises. The problem is the most obvious with Map-s, where myObj.name in the template can mean both myObject.getName() or myObject.get("name") in Java. And then, what should we return when you list myObject with #list? Should we return a mix of Map keys, and Java method names? Things like that.
The secondary reason is hiding the technical details of the model. If one day there's change in the data-model where something that was once an array is now List, your templates won't break, because the template never seens them as a List or an array, but as a "sequence", which is the native type of the template language. By the way, in the rare case where you still want to access the Java API, you can use ?api, like this: team?api.name As a historical perspective, FreeMarker originally didn't support accessing properties, or calling methods. You had to put together the data-model explicitly from the native types. On Tue, Mar 25, 2025 at 7:01 PM Niels Basjes <ni...@basjes.nl> wrote: > Hi, > > Thanks, this really helps. > > Just curious; What is the reason for making this distinction? > Why does the DefaultObjectWrapper expose the getters of a Pojo, but it does > not expose them if it implements one of the mentioned collections? > Is it possible to combine the two effects and do both (both making it #list > capable and getting the custom fields)? > > Niels > > On Mon, Mar 24, 2025 at 9:28 PM Daniel Dekany <daniel.dek...@gmail.com> > wrote: > > > It's because if Team implements Set, List, Map, etc., then, by default, > it > > will only see the Collection/Map elements, and not the Java methods of > the > > object. > > > > Probably the cleanest/idiomatic solution is if Team is not a Set, but > > instead has a method like Set<User> getUsers(). Regardless of FreeMarker, > > that's what I would do as a Java programmer. > > > > But if it must be a Set, you can force FreeMarker to ignore that like > this: > > > > So you can do this: > > > > public static class CustomObjectWrapper extends DefaultObjectWrapper > { > > public CustomObjectWrapper(Version incompatibleImprovements) { > > super(incompatibleImprovements); > > } > > > > @Override > > public TemplateModel wrap(Object obj) throws > TemplateModelException > > { > > if (obj instanceof Team) { > > return new GenericObjectModel(obj, this); > > } > > return super.wrap(obj); > > } > > } > > > > and then where you create your Configuration singleton: > > > > configuration.setObjectWrapper(new > > CustomObjectWrapper(Configuration.VERSION_2_3_35)); > > > > Although now that being a Set was ignored, team is not #list-able in the > > template (except with directly calling the Set API-s). > > > > > > On Mon, Mar 24, 2025 at 12:00 PM Niels Basjes <ni...@basjes.nl> wrote: > > > > > Hi, > > > > > > I'm new to Freemarker and I'm looking for the right solution for > > > something I ran into but have not been able to figure out. > > > > > > If I have a Java class like this: > > > > > > public static class Team { > > > private final String name; > > > public Team(String name) { > > > this.name = name; > > > } > > > public String getName() { > > > return name; > > > } > > > } > > > > > > If I then make a TemplateTest like this > > > > > > @Test > > > public void testTeamName() throws Exception { > > > Team team = new Team("Working"); > > > addToDataModel("team", team); > > > assertEquals("Working", team.getName()); > > > assertOutput("${team.name}","Working"); > > > } > > > > > > and it all works. > > > If I change my Team to be a subclass of a well known collection (I have > > > tried TreeMap, TreeSet and ArrayList) then my .name attribute is no > > longer > > > available in a template. > > > > > > public static class TeamSet extends TreeSet<User> { > > > > > > > > > What is the proper way to solve this? > > > Perhaps a custom Object Wrapper? > > > Or is this something I should report as a bug? > > > > > > Thanks. > > > > > > Niels Basjes > > > nielsbas...@apache.org > > > ni...@basjes.nl > > > > > > > > > -- > > Best regards, > > Daniel Dekany > > > -- Best regards, Daniel Dekany