How to make JinJava throw error if it can't find a binding

765 Views Asked by At

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?

2

There are 2 best solutions below

0
On BEST ANSWER

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}
0
On

You can do something like this,

JinjavaConfig jc = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build();
Jinjava jinjava = new Jinjava(jc);

// set context and other processes and then render
RenderResult renderResult = jinjava.renderForResult(template, properties);
if (renderResult.getErrors().isEmpty()) {
    return renderResult.getOutput();
} else {
    throw new IllegalArgumentException(renderResult.getErrors().stream()
            .map(Object::toString)
            .collect(Collectors.joining(",")));
}

There are many other JinjavaConfig that you might want to check.