Here is my object class:
public class Address
{
public final String line1;
public final String town;
public final String postcode;
public Address(final String line1, final String town, final String postcode)
{
this.line1 = line1;
this.town = town;
this.postcode = postcode;
}
}
I add it to the velocity context like this:
Address theAddress = new Address("123 Fake St", "Springfield", "SP123");
context.put("TheAddress", theAddress);
However, when writing the template, the following will not render the address fields (however, it works fine when I add getters to the Address class)
<Address>
<Line1>${TheAddress.line1}</Line1>
<Town>${TheAddress.town}</Town>
<Postcode>${TheAddress.postcode}</Postcode>
</Address>
Is it possible to access public fields on objects from Velocity without adding getters?
Not by default. You need to configure a different Uberspect implementation.
The Velocity user guide suggests it's not possible. Quote:
[Velocity] tries out different alternatives based on several established naming conventions. The exact lookup sequence depends on whether or not the property name starts with an upper-case letter. For lower-case names, such as $customer.address, the sequence is
- getaddress()
- getAddress()
- get("address")
- isAddress()
For upper-case property names like $customer.Address, it is slightly different:
- getAddress()
- getaddress()
- get("Address")
- isAddress()
http://wiki.apache.org/velocity/VelocityFAQ:
Q: How can i access my object's public fields in my templates?
A: Currently, you have three options:
Wrap your object with a FieldMethodizer
Configure your VelocityEngine to use a custom uberspector like the PublicFieldUberspect
Lobby the velocity-dev list to add public field introspection as a default fallback if no matching method is found :)
FieldMethodizer
works only with public static fields.
PublicFieldUberspect
example code is quite old and it just fails with error on nonexistent fields.
And forget about lobby at dev list. )
Meanwhile, there is good caching implementation of UberspectPublicFields in current velocity trunk. Unfortunately, there was no active development for years and no plans for next release are published.
One would have to build it himself and bundle in private repository.
Another altervative is a fork with bonus scala compatibility that is available in central maven repository:
http://maven-repository.com/artifact/com.sksamuel.scalocity/scalocity/0.9.
Drop in instead of usual velocity dependency:
<dependency>
<groupId>com.sksamuel.scalocity</groupId>
<artifactId>scalocity</artifactId>
<version>0.9</version>
</dependency>
Then just add to velocity.properties
:
runtime.introspector.uberspect = org.apache.velocity.util.introspection.UberspectPublicFields, org.apache.velocity.util.introspection.UberspectImpl
The caveat is that UberspectImpl
is patched with additional support for scala properties and requires 8 MB scala jar.
Eventually, I just interned the following classes from velocity trunk into own project:
org.apache.velocity.runtime.parser.node.PublicFieldExecutor
org.apache.velocity.runtime.parser.node.SetPublicFieldExecutor
org.apache.velocity.util.introspection.ClassFieldMap
org.apache.velocity.util.introspection.Introspector
org.apache.velocity.util.introspection.IntrospectorBase
org.apache.velocity.util.introspection.IntrospectorCache
org.apache.velocity.util.introspection.IntrospectorCacheImpl
org.apache.velocity.util.introspection.UberspectPublicFields
These work fine with Velocity 1.7.
i do
import org.apache.velocity.util.introspection.UberspectImpl;
import org.apache.velocity.util.introspection.UberspectPublicFields;
....
properties.setProperty("runtime.introspector.uberspect",
UberspectImpl.class.getName() + ", " +
UberspectPublicFields.class.getName());
And all works OK!!!