如何内省自由标记模板以找出它使用的变量?

Jar*_*red 18 java freemarker

我完全不确定这是否是一个可解决的问题,但假设我有一个freemarker模板,我希望能够向模板询问它使用的变量.

出于我的目的,我们可以假设freemarker模板非常简单 - 只是"根级"条目(这种模板的模型可以是一个简单的Map).换句话说,我不需要处理调用嵌套结构等的模板.

Set*_*thu 6

从java获取变量的另一种方法。这只是尝试处理模板并捕获InvalidReferenceException以查找 freemarker-template 中的所有变量

 /**
 * Find all the variables used in the Freemarker Template
 * @param templateName
 * @return
 */
public Set<String> getTemplateVariables(String templateName) {
    Template template = getTemplate(templateName);
    StringWriter stringWriter = new StringWriter();
    Map<String, Object> dataModel = new HashMap<>();
    boolean exceptionCaught;

    do {
        exceptionCaught = false;
        try {
            template.process(dataModel, stringWriter);
        } catch (InvalidReferenceException e) {
            exceptionCaught = true;
            dataModel.put(e.getBlamedExpressionString(), "");
        } catch (IOException | TemplateException e) {
            throw new IllegalStateException("Failed to Load Template: " + templateName, e);
        }
    } while (exceptionCaught);

    return dataModel.keySet();
}

private Template getTemplate(String templateName) {
    try {
        return configuration.getTemplate(templateName);
    } catch (IOException e) {
        throw new IllegalStateException("Failed to Load Template: " + templateName, e);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我见过的唯一一种 A) 有效且 B) 不使用已弃用的 FreeMarker 方法的方法。太棒了。 (4认同)

小智 5

这可能是迟到的,但是如果其他人遇到这个问题:您可以使用'data_model'和'globals'来检查模型 - data_model将只包含模型提供的值,而globals也将包含模板中定义的任何变量.你需要在点前加上特殊变量 - 所以要访问全局变量,请使用$ {.globals}

有关其他特殊变量,请参阅http://freemarker.sourceforge.net/docs/ref_specvar.html

  • 实际上,我想在我的 Java 代码中获取模板中的变量列表。我希望能够执行以下操作:模板 t; .... List&lt;Variable&gt; = t.getReferenceVariables(); (2认同)
  • 不幸的是,虽然它有效,但不推荐使用`getRootTreeNode()`.什么是替代品? (2认同)

Sim*_*mY4 3

我有同样的任务从 java 端的模板获取变量列表,但除了使用反射之外,没有找到任何好的方法。我不确定是否有更好的方法来获取这些数据,但这是我的方法:

public Set<String> referenceSet(Template template) throws TemplateModelException {
    Set<String> result = new HashSet<>();
    TemplateElement rootTreeNode = template.getRootTreeNode();
    for (int i = 0; i < rootTreeNode.getChildCount(); i++) {
        TemplateModel templateModel = rootTreeNode.getChildNodes().get(i);
        if (!(templateModel instanceof StringModel)) {
            continue;
        }
        Object wrappedObject = ((StringModel) templateModel).getWrappedObject();
        if (!"DollarVariable".equals(wrappedObject.getClass().getSimpleName())) {
            continue;
        }

        try {
            Object expression = getInternalState(wrappedObject, "expression");
            switch (expression.getClass().getSimpleName()) {
                case "Identifier":
                    result.add(getInternalState(expression, "name").toString());
                    break;
                case "DefaultToExpression":
                    result.add(getInternalState(expression, "lho").toString());
                    break;
                case "BuiltinVariable":
                    break;
                default:
                    throw new IllegalStateException("Unable to introspect variable");
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new TemplateModelException("Unable to reflect template model");
        }
    }
    return result;
}

private Object getInternalState(Object o, String fieldName) throws NoSuchFieldException, IllegalAccessException {
    Field field = o.getClass().getDeclaredField(fieldName);
    boolean wasAccessible = field.isAccessible();
    try {
        field.setAccessible(true);
        return field.get(o);
    } finally {
        field.setAccessible(wasAccessible);
    }
}
Run Code Online (Sandbox Code Playgroud)

我为演示模板自省而制作的示例项目可以在 github 上找到: https: //github.com/SimY4/TemplatesPOC.git

  • @MichaelOryl 嗨。我知道我的方法使用了已弃用的 API,我个人不建议使用它。但它仍然对我有用,即使是在 2016 年和 freemarker 2.3.25 中。您可以在 https://github.com/SimY4/TemplatesPOC.git 查看工作代码 (2认同)