22

我完全不确定这甚至是一个可解决的问题,但假设我有一个 freemarker 模板,我希望能够询问该模板它使用了哪些变量。

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

4

7 回答 7

6

这可能为时已晚,但万一其他人遇到此问题:您可以使用“data_model”和“globals”来检查模型 - data_model 将仅包含模型提供的值,而 globals 还将包含模板中定义的任何变量。您需要在特殊变量前面加上一个点 - 所以要访问全局变量,请使用 ${.globals}

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

于 2010-08-10T16:58:21.020 回答
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);
    }
}
于 2016-08-08T19:43:52.557 回答
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);
    }
}

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

于 2015-08-31T07:29:58.187 回答
0

我为我非常简单的用例解决了这个问题(仅使用平面数据结构,没有嵌套(例如:${parent.child}),列表或更具体)与虚拟数据提供者:

public class DummyDataProvider<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = 1;

    public final Set<String> variables = new HashSet<>();

    @SuppressWarnings("unchecked")
    @Override
    public V get(Object key) {
        variables.add(key.toString());
        return (V) key;
    }
}

您可以将此用于处理模板,并且当它完成时 Setvariables包含您的变量。

这是一种非常简单的方法,当然需要改进,但你明白了。

于 2016-12-06T14:06:08.207 回答
0
public static Set<String> getNecessaryTemplateVariables(String templateName) throws TemplateModelException {
        Set<String> result = new HashSet<>();
        TemplateElement rootTreeNode = getTemplate(templateName).getRootTreeNode();
        if ("IteratorBlock".equals(rootTreeNode.getClass().getSimpleName())) {
            introspectFromIteratorBlock(result, rootTreeNode);
            return result;
        }
        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())) {
                introspectFromDollarVariable(result, wrappedObject);
            } else if ("ConditionalBlock".equals(wrappedObject.getClass().getSimpleName())) {
                introspectFromConditionalBlock(result, wrappedObject);
            } else if ("IfBlock".equals(wrappedObject.getClass().getSimpleName())) {
                introspectFromIfBlock(result, wrappedObject);
            } else if ("IteratorBlock".equals(wrappedObject.getClass().getSimpleName())) {
                introspectFromIteratorBlock(result, wrappedObject);
            }
        }
        return result;
    }

    private static void introspectFromIteratorBlock(Set<String> result, Object wrappedObject) {
        try {
            Object expression = getInternalState(wrappedObject, "listExp");
            result.add(getInternalState(expression, "name").toString());
        } catch (NoSuchFieldException | IllegalAccessException ignored) {
        }
    }

    private static void introspectFromConditionalBlock(Set<String> result, Object wrappedObject)
        throws TemplateModelException {
        try {
            Object expression = getInternalState(wrappedObject, "condition");
            if (expression == null) {
                return;
            }
            result.addAll(dealCommonExpression(expression));
            String nested = getInternalState(wrappedObject, "nestedBlock").toString();
            result.addAll(getNecessaryTemplateVariables(nested));
        } catch (NoSuchFieldException | IllegalAccessException ignored) {
        }
    }

    private static Set<String> dealCommonExpression(Object expression)
        throws NoSuchFieldException, IllegalAccessException {
        Set<String> ret = Sets.newHashSet();
        switch (expression.getClass().getSimpleName()) {
            case "ComparisonExpression":
                String reference = dealComparisonExpression(expression);
                ret.add(reference);
                break;
            case "ExistsExpression":
                reference = dealExistsExpression(expression);
                ret.add(reference);
                break;
            case "AndExpression":
                ret.addAll(dealAndExpression(expression));
            default:
                break;
        }
        return ret;
    }

    private static String dealComparisonExpression(Object expression)
        throws NoSuchFieldException, IllegalAccessException {
        Object left = getInternalState(expression, "left");
        Object right = getInternalState(expression, "right");
        String reference;
        if ("Identifier".equals(left.getClass().getSimpleName())) {
            reference = getInternalState(left, "name").toString();
        } else {
            reference = getInternalState(right, "name").toString();
        }
        return reference;
    }

    private static String dealExistsExpression(Object expression) throws NoSuchFieldException, IllegalAccessException {
        Object exp = getInternalState(expression, "exp");
        return getInternalState(exp, "name").toString();
    }

    private static Set<String> dealAndExpression(Object expression)  throws NoSuchFieldException,
        IllegalAccessException{
        Set<String> ret = Sets.newHashSet();
        Object lho = getInternalState(expression, "lho");
        ret.addAll(dealCommonExpression(lho));
        Object rho = getInternalState(expression, "rho");
        ret.addAll(dealCommonExpression(rho));
        return ret;
    }

    private static void introspectFromIfBlock(Set<String> result, Object wrappedObject)
        throws TemplateModelException {
        result.addAll(getNecessaryTemplateVariables(wrappedObject.toString()));
    }

    private static void introspectFromDollarVariable(Set<String> result, Object wrappedObject)
        throws TemplateModelException {
        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:
                    break;
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new TemplateModelException("Unable to reflect template model");
        }
    }

    private static Object getInternalState(Object o, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field [] fieldArray = o.getClass().getDeclaredFields();
        for (Field field : fieldArray) {
            if (!field.getName().equals(fieldName)) {
                continue;
            }
            boolean wasAccessible = field.isAccessible();
            try {
                field.setAccessible(true);
                return field.get(o);
            } finally {
                field.setAccessible(wasAccessible);
            }
        }
        throw new NoSuchFieldException();
    }

    private static Template getTemplate(String templateName) {
        try {
            StringReader stringReader = new StringReader(templateName);
            return new Template(null, stringReader, null);
        } catch (IOException e) {
            throw new IllegalStateException("Failed to Load Template: " + templateName, e);
        }
    }

我优化了 SimY4 的答案,并支持 <#list> 和 <#if> 块。代码未经过全面测试

于 2020-04-26T12:05:11.000 回答
-1

在模板上执行以下正则表达式:

(?<=\$\{)([^\}]+)(?=\})
  • (?<=\$\{) 匹配后跟 ${ 的所有内容
  • ([^\}]+) 匹配任何不包含 } 的字符串
  • (?=\}) 匹配 } 之前的所有内容
于 2015-04-14T09:39:01.823 回答
-1

我遇到了同样的问题,发布的解决方案对我来说都没有意义。我带来的是插入 TemplateExceptionHandler. 例子:

final private List<String> missingReferences = Lists.newArrayList();
final Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setTemplateExceptionHandler(new TemplateExceptionHandler() {

    @Override
    public void handleTemplateException(TemplateException arg0, Environment arg1, Writer arg2) throws TemplateException {
        if (arg0 instanceof InvalidReferenceException) {
            missingReferences.add(arg0.getBlamedExpressionString());
            return;
        }
        throw arg0;
    }

}

Template template = loadTemplate(cfg, templateId, templateText);

StringWriter out = new StringWriter();

try {
    template.process(templateData, out);
} catch (TemplateException | IOException e) {
        throw new RuntimeException("oops", e);
}

System.out.println("Missing references: " + missingReferences);

在此处阅读更多信息:https ://freemarker.apache.org/docs/pgui_config_errorhandling.html

于 2017-12-29T14:52:12.310 回答