如何在Freemarker中递归地从模板数据 Map 对象中打印数据?

huangapple 未分类评论55阅读模式
英文:

How to recursively Print data from template data Map Object in Freemarker?

问题

我们有一个需求,需要将类型为 Map<String,List> 的对象传递给 Freemarker 模板。这里的问题是列表内部的对象可以是 List、Map、自定义对象,或者只是一个简单的字符串。列表和映射类型可以进一步嵌套,类似于以下示例:

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

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

我想知道是否有办法可以在 Freemarker 中直接实现这一点,或者通过为 Freemarker 的任何类/接口提供自定义实现,或者通过一些配置更改来实现这一点。

英文:

We have a requirement where we need to pass object of type Map<String,List> to freemarker template. Here the issue is the Object inside a list can be a List, a Map or custom object or just a simple string. List and Map type can be further nested.something like below.

Map&lt;String,Object&gt; templateData = new HashMap&lt;&gt;();
templateData.put(&quot;complexKey&quot;,&quot;ABC&quot;);
        //or
templateData.put(&quot;complexKey&quot;,new List&lt;String&gt;());
       //or
templateData.put(&quot;complexKey&quot;,new List&lt;Map&lt;String,List&lt;String&gt;&gt;&gt;());

I need to find a way to identify the type of Object and apply some recursive solution until I find the suitable object to print.

I need to know if there is a way we can achieve this in free marker directly or through providing custom implementation of any class/interface from freemarker or through some configuration changes.

答案1

得分: 0

我真的对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("访问 " + node.toString()));
        }
    
        public static void walk(Map node, Consumer lambda) {
            System.out.println("Map对象: " + node);
            node.forEach((k, v) -> handleNode(v, lambda));
        }
    
        public static void walk(Collection node, Consumer lambda) {
            System.out.println("可迭代对象: " + node);
            node.forEach(v -> handleNode(v, lambda));
        }
    
        public static void walk(Object catchall, Consumer lambda) {
            System.out.println("通用对象: " + catchall);
            lambda.accept(catchall);
        }
    
        private static void handleNode(Object v, Consumer lambda) {
            System.out.println("处理类型为" + 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表达式可以收集统计数据打印内容等应用于所有节点如果需要可以通过为每个节点都提供前处理和后处理的Consumer来增强这个过程

由于我们在处理对象时丢失了强制转换的信息我们需要在handleNode中重新注入这些信息我本来希望能有更简洁的方法但可能需要更多的咖啡来思考
英文:

I really don't know anything about freemarker, but if you can get your hooks into it to process via straight Java you can try this:

package com.jdluke.treewalker;

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

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

    public static void walk(Map node, Consumer lambda) {
        System.out.println(&quot;Map object: &quot; + node);
        node.forEach((k, v) -&gt; handleNode(v, lambda));
    }

    public static void walk(Collection node, Consumer lambda) {
        System.out.println(&quot;Iterable object: &quot; + node);
        node.forEach(v -&gt; handleNode(v, lambda));
    }

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

    private static void handleNode(Object v, Consumer lambda) {
        System.out.println(&quot;Handling object of type&quot; + 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);
        }
    }
}

What's happening here is that the various overloaded 'walk' methods have their own ways of dealing with their current location in the mixed tree. The lambda (which can gather stats, print stuff, or whatever) is applied to all nodes. This could be enhanced if necessary by having both a pre- and a post- processing Consumer for each node.

Since we lose cast information when we 'handle' an object we need to sort of reinject that in handleNode. I was hoping for something cleaner but may need more coffee.

答案2

得分: 0

在模板内部,根据我所看到的内容,似乎是可行的。

FreeMarker 宏和函数支持递归。

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

英文:

Looks doable inside the template based on what I see.

Recursion is supported for FreeMarker macros and functions.

To check the type of a value you can use value?is_sequence (for List), value?is_hash_ex (for Map), and ?is_string (for String). (Actually this depends on the ObjectWrapper set inside the Configuration, but on almost all real world configuration this will work.)

huangapple
  • 本文由 发表于 2020年7月27日 18:17:14
  • 转载请务必保留本文链接:https://java.coder-hub.com/63113230.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定