66

What is the best way to create Velocity Template from a String?

I'm aware of Velocity.evaluate method where I can pass String or StringReader, but I'm curios is there a better way to do it (e.g. any advantage of creating an instance of Template).

tomsame
  • 810
  • 1
  • 7
  • 10

7 Answers7

88

There is some overhead parsing template. You might see some performance gain by pre-parsing the template if your template is large and you use it repeatedly. You can do something like this,

RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
StringReader reader = new StringReader(bufferForYourTemplate);
Template template = new Template();
template.setRuntimeServices(runtimeServices);

/*
 * The following line works for Velocity version up to 1.7
 * For version 2, replace "Template name" with the variable, template
 */
template.setData(runtimeServices.parse(reader, "Template name")));

template.initDocument();

Then you can call template.merge() over and over again without parsing it everytime.

BTW, you can pass String directly to Velocity.evaluate().

Frank R.
  • 1,732
  • 18
  • 21
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • Exactly what I was looking for. Thanks. For other people's reference, runtimeServices is an instance of org.apache.velocity.runtime.RuntimeInstance – tomsame Sep 16 '09 at 14:00
  • Missed one-line. For completeness, I added it. – ZZ Coder Sep 16 '09 at 14:13
  • 17
    +1 for the mention of Velocity.Evaluate since that's exactly what I was looking for. – Bernhard Hofmann Feb 09 '11 at 10:51
  • compilation error due to too many parens. should be `template.setData(runtimeServices.parse(reader, "Template name"));` stackoverflow complains `Edits must be at least 6 characters; is there something else to improve in this post?`. – joseph Apr 16 '21 at 21:51
21

The above sample code is working for me. It uses Velocity version 1.7 and log4j.

private static void velocityWithStringTemplateExample() {
    // Initialize the engine.
    VelocityEngine engine = new VelocityEngine();
    engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.Log4JLogChute");
    engine.setProperty("runtime.log.logsystem.log4j.logger", LOGGER.getName());
    engine.setProperty(Velocity.RESOURCE_LOADER, "string");
    engine.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
    engine.addProperty("string.resource.loader.repository.static", "false");
    //  engine.addProperty("string.resource.loader.modificationCheckInterval", "1");
    engine.init();

    // Initialize my template repository. You can replace the "Hello $w" with your String.
    StringResourceRepository repo = (StringResourceRepository) engine.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT);
    repo.putStringResource("woogie2", "Hello $w");

    // Set parameters for my template.
    VelocityContext context = new VelocityContext();
    context.put("w", "world!");

    // Get and merge the template with my parameters.
    Template template = engine.getTemplate("woogie2");
    StringWriter writer = new StringWriter();
    template.merge(context, writer);

    // Show the result.
    System.out.println(writer.toString());
}

A similar so question.

Community
  • 1
  • 1
Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92
  • Wow, this is a unique approach. how velocity engine load resource seems is internal implementation, could property "string.resource.loader.class" subject to change? – Ben.Yan Aug 02 '17 at 20:30
9

This works in Velocity 2.1

// Initialize the engine
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.setProperty(Velocity.RESOURCE_LOADERS, "string");
velocityEngine.setProperty("resource.loader.string.class", StringResourceLoader.class.getName());
velocityEngine.setProperty("resource.loader.string.cache", true);
velocityEngine.setProperty("resource.loader.string.modification_check_interval", 60);
velocityEngine.init();

// Add template to repository
StringResourceRepository repository = StringResourceLoader.getRepository();
repository.putStringResource("hello_world", "Hello $w");

// Set parameters
VelocityContext context = new VelocityContext();
context.put("w", "world!");

// Process the template
StringWriter writer = new StringWriter();
velocityEngine.getTemplate("hello_world").merge( context, writer );

System.out.println(writer.toString());
devatherock
  • 2,423
  • 1
  • 8
  • 23
  • 1
    You seem to have a typo in `Velocity.RESOURCE_LOADERS` - should probably be `Velocity.RESOURCE_LOADER`. – vektor Feb 27 '20 at 13:39
  • The `RuntimeConstants` interface which is implemented by `Velocity` class has both constants - `RESOURCE_LOADERS` and `RESOURCE_LOADER`. `RESOURCE_LOADERS` was the one I tested with – devatherock Mar 03 '20 at 02:11
  • This worked for me for some time. But then I found internal Velocity exceptions in the log file. I changed to the following solution, and everything works fine now: [Velocity template as a String in Java](https://stackoverflow.com/a/55064212/5223047). It uses [Velocity#evaluate(context, out, logTag, instring)](https://velocity.apache.org/engine/2.0/apidocs/org/apache/velocity/app/Velocity.html#evaluate-org.apache.velocity.context.Context-java.io.Writer-java.lang.String-java.lang.String-), as mentioned in another answer here. – ltlBeBoy Jan 15 '21 at 21:54
2
RuntimeServices rs = RuntimeSingleton.getRuntimeServices();            
StringReader sr = new StringReader("Username is $username");
SimpleNode sn = rs.parse(sr, "User Information");

Template t = new Template();
    t.setRuntimeServices(rs);
    t.setData(sn);
    t.initDocument();

VelocityContext vc = new VelocityContext();
vc.put("username", "John");

StringWriter sw = new StringWriter();
t.merge(vc, sw);

System.out.println(sw.toString());
otmek
  • 21
  • 1
1

Velocity 2 can be integrated into the JSR223 Java Scripting Language Framework which make another option to transform string as a template:

ScriptEngineManager manager = new ScriptEngineManager();
manager.registerEngineName("velocity", new VelocityScriptEngineFactory());
ScriptEngine engine = manager.getEngineByName("velocity");


System.setProperty(VelocityScriptEngine.VELOCITY_PROPERTIES, "path/to/velocity.properties");
String script = "Hello $world";
Writer writer = new StringWriter();
engine.getContext().setWriter(writer);
Object result = engine.eval(script);
System.out.println(writer);
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
1

If you are looking for just variable substitution then following works

public String velocityEvaluate(final String template, final NotificationDTO notificationDTO) {
    final Map<String, String> context = getContextMap(notificationDTO);
    final VelocityContext velocityContext = new VelocityContext(context);
    final StringWriter stringWriter = new StringWriter();
    final StringReader reader = new StringReader(template);
    Velocity.evaluate(velocityContext, stringWriter, "Velocity String Template Evaluation", reader);
    return stringWriter.toString();
}
Rajat
  • 2,467
  • 2
  • 29
  • 38
0

In case if any one looking to transform from json string to json object, in that case, need to convert json string to JsonNode & store it in context. For Ex:

String jsonDataAsString = "{"name": "Aps"}";
JsonNode nodes = new ObjectMapper().readTree(jsonDataAsString );
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("root", nodes);

then in your template, you can refer originating data which are set as "root" via "$root."+property

$root.name

Hope it helps someone.