简体   繁体   English

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

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

We have a requirement where we need to pass object of type Map<String,List> to freemarker template.我们有一个要求,我们需要将 Map<String,List> 类型的 object 传递给 freemarker 模板。 Here the issue is the Object inside a list can be a List, a Map or custom object or just a simple string.这里的问题是列表中的 Object 可以是列表、Map 或自定义 object 或只是一个简单的字符串。 List and Map type can be further nested.something like below.列表和 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>>>());

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.我需要找到一种方法来识别 Object 的类型并应用一些递归解决方案,直到找到合适的 object 进行打印。

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.我需要知道是否有一种方法可以直接在免费标记中实现这一点,或者通过从 freemarker 提供任何类/接口的自定义实现或通过一些配置更改来实现。

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:我真的对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);
        }
    }
}

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.这里发生的是各种重载的“walk”方法有自己的方式来处理它们在混合树中的当前位置。 The lambda (which can gather stats, print stuff, or whatever) is applied to all nodes. lambda(可以收集统计数据、打印数据或其他)适用于所有节点。 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.因为当我们“处理”一个 object 时我们会丢失转换信息,所以我们需要在 handleNode 中重新注入它。 I was hoping for something cleaner but may need more coffee.我希望有更清洁的东西,但可能需要更多的咖啡。

Looks doable inside the template based on what I see.根据我所看到的,在模板内看起来可行。

Recursion is supported for FreeMarker macros and functions. FreeMarker 宏和函数支持递归。

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 ).要检查值的类型,您可以使用value?is_sequence (对于List )、 value?is_hash_ex (对于Map )和?is_string (对于String )。 (Actually this depends on the ObjectWrapper set inside the Configuration , but on almost all real world configuration this will work.) (实际上,这取决于Configuration中的ObjectWrapper集,但在几乎所有现实世界的配置中,这都会起作用。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM