如何从 Freemarker 中的模板数据映射对象递归打印数据?

Posted

技术标签:

【中文标题】如何从 Freemarker 中的模板数据映射对象递归打印数据?【英文标题】:How to recursively Print data from template data Map Object in Freemarker? 【发布时间】:2020-11-16 15:51:21 【问题描述】:

我们需要将 Map 类型的对象传递给 freemarker 模板。这里的问题是列表中的对象可以是列表、地图或自定义对象,或者只是一个简单的字符串。 List 和 Map 类型可以进一步嵌套。如下所示。

Map<String,Object> templateData = new HashMap<>();
templateData.put("complexKey","ABC");
        //or
templateData.put("complexKey",new List<String>());
       //or
templateData.put("complexKey",new List<Map<String,List<String>>>());

我需要找到一种方法来识别对象的类型并应用一些递归解决方案,直到找到合适的对象进行打印。

我需要知道是否有一种方法可以直接在 freemarker 中实现这一点,或者通过从 freemarker 提供任何类/接口的自定义实现或通过一些配置更改。

【问题讨论】:

【参考方案1】:

我真的对freemarker一无所知,但如果你能把你的钩子放进去通过直接的Java处理,你可以试试这个:

package com.jdluke.treewalker;

import java.util.*;
import java.util.function.Consumer;

public class TreeWalker 
    public static void main(String[] args) 
        Map<String, Object> mainMap = new HashMap();
        mainMap.put("stringNode", "This is just a String");
        mainMap.put("listNode", Arrays.asList("one", "two"));
        Map mapNode1 = new HashMap();
        Map mapNode2 = new HashMap();
        Map mapNode3 = new HashMap();
        mapNode1.put("mapnode1.1", "first element");
        mapNode1.put("mapnode1.2", "second element");
        mapNode2.put("mapnode2.1", Arrays.asList("three", "four"));
        mapNode3.put("mapnode3.1", "map node 3, element 1");
        mapNode3.put("mapnode3.2", "map node 3, element 2");
        mainMap.put("listNode2", Arrays.asList(mapNode1, mapNode2));
        mainMap.put("mapNode", mapNode3);
        int count = 0;
        walk(mainMap, (node) -> System.out.println("Visiting " + node.toString()));
    

    public static void walk(Map node, Consumer lambda) 
        System.out.println("Map object: " + node);
        node.forEach((k, v) -> handleNode(v, lambda));
    

    public static void walk(Collection node, Consumer lambda) 
        System.out.println("Iterable object: " + node);
        node.forEach(v -> handleNode(v, lambda));
    

    public static void walk(Object catchall, Consumer lambda) 
        System.out.println("Catchall object: " + catchall);
        lambda.accept(catchall);
    

    private static void handleNode(Object v, Consumer lambda) 
        System.out.println("Handling object of type" + v.getClass().toString());
        lambda.accept(v);
        if (v instanceof Collection) 
            walk((Collection) v, lambda);
         else if (v instanceof Map) 
            walk((Map) v, lambda);
         else 
            walk(v, lambda);
        
    

这里发生的情况是,各种重载的“walk”方法有自己的方式来处理它们在混合树中的当前位置。 lambda(可以收集统计信息、打印内容或其他)应用于所有节点。如有必要,可以通过为每个节点设置一个预处理消费者和一个后处理消费者来增强这一点。

由于我们在“处理”一个对象时丢失了转换信息,我们需要在 handleNode 中重新注入。我希望有更清洁的东西,但可能需要更多的咖啡。

【讨论】:

【参考方案2】:

根据我所见,在模板内看起来可行。

FreeMarker 宏和函数支持递归。

要检查值的类型,您可以使用value?is_sequence(用于List)、value?is_hash_ex(用于Map)和?is_string(用于String)。 (实际上这取决于Configuration 中设置的ObjectWrapper,但在几乎所有现实世界的配置中,这都可以。)

【讨论】:

以上是关于如何从 Freemarker 中的模板数据映射对象递归打印数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 FreeMarker 模板调用对象的 java 方法?

在 Freemarker 模板中,如何获取数据模型类的名称?

如何从freemarker模板中获取当前年份

将对象列表传递给 Freemarker 然后循环

将对象列表传递给Freemarker然后循环

freemarker如何遍历模板中所有变量