I need to use JinJava to process small templates that can contain different tokens. For example:
Hello,{{ user }}...
where a binding user
is required because without it the output does not make any sense.
My class does not know what tokens template uses and it gets a list of bindings from remote services.
Therefore, there may be a case when a binding that is specified in a template does not exist. JinJava's default behavior is to use null
that is converted to empty string in the output string. It does not work in my case, I need an exception to be thrown if any of bindings is missed.
My current solution is to use a custom Filter
/**
* A filter for JinJava that verifies that a value is not null and if the value is null, writes a fatal error to interpreter's log
*/
public class RequiredFilter implements Filter {
@Override
public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
if (var == null) {
interpreter.addError(new TemplateError(TemplateError.ErrorType.FATAL, TemplateError.ErrorReason.MISSING, "Value of a required key is null", "", 1, null));
}
return var;
}
@Override
public String getName() {
return "required";
}
}
And make clients to explicitly specify this function for all tokens in templates. The example above now looks like:
Hello, {{ user|required }}...
and if the binding user
does not exist, I get my exception. However, the requirement of a custom function looks strange.
Is there a way to set up JinJava to achieve the similar behavior and do not bother client by the requirement to specify the filter required
every time?
Based on your solution with filters of tokens i did it by registering custom Tags for that purpose.
public static String fillTemplateTagsWithProperties(
String template, Map<String, String> properties) throws IllegalArgumentException
{
if (template == null) throw new IllegalArgumentException("template is null");
Jinjava jinjava = new Jinjava();
properties.forEach((k, v) -> jinjava.getGlobalContext().registerTag(new Tag() {
@Override
public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
if (v == null) {
interpreter.addError(new TemplateError(
TemplateError.ErrorType.FATAL,
TemplateError.ErrorReason.MISSING,
"Value of a required key is null",
k, 1, null));
}
return v;
}
@Override
public String getEndTagName() {
return null;
}
@Override
public String getName() {
return k;
}
}));
RenderResult renderResult = jinjava.renderForResult(template, properties);
if (renderResult.getErrors().size() == 0) {
return renderResult.getOutput();
} else {
throw new IllegalArgumentException(renderResult.getErrors().stream()
.map(Object::toString)
.collect(Collectors.joining(",")));
}
}
@Test
public void jinjavaWithCustomTags(){
String template = "Hallo {% hallo %}\n" +
"I am {% unrendered %}";
Map<String, String> props = new HashMap<>();
props.put("hallo", "Welt");
//props.put("unrendered", "[try null here]");
System.out.println(fillTemplateTagsWithProperties(template, props));
}
which outputs:
Hallo Welt
I am [try null here]
or throws:
### java.lang.IllegalArgumentException: TemplateError{severity=FATAL, reason=SYNTAX_ERROR, message=UnknownTagException: Syntax error in '{% unrendered %}': Unknown tag: unrendered, fieldName=null, lineno=2, item=OTHER}