Java 当使用#递归树而不是XML时,我需要数据模型的Freemarker文档

Java 当使用#递归树而不是XML时,我需要数据模型的Freemarker文档,java,freemarker,Java,Freemarker,我试图在Freemarker中处理树结构,并希望使用#recurse,#visit指令,但我找不到任何关于如何设置数据模型的好文档。我能看到的唯一例子是那些为XML结构创建数据模型的例子。我不需要这么详细。我的树很简单 在尝试测试我需要的功能时,我构建了一个单元测试,但当我运行它时,我得到了 FreeMarker template error: For "." left-hand operand: Expected a hash, but this has evaluated to a node

我试图在Freemarker中处理树结构,并希望使用#recurse,#visit指令,但我找不到任何关于如何设置数据模型的好文档。我能看到的唯一例子是那些为XML结构创建数据模型的例子。我不需要这么详细。我的树很简单

在尝试测试我需要的功能时,我构建了一个单元测试,但当我运行它时,我得到了

FreeMarker template error:
For "." left-hand operand: Expected a hash, but this has evaluated to a node
以下是源代码:

public class FreemarkerXmlTests {

    static class Element implements TemplateNodeModel {
        private final String name;
        private final String text;
        private Element parent;
        private final List<Element> elements = new ArrayList<>();

        public Element(String name) {
            this(name, null);
        }

        public Element(String name, String text) {
            this.name = name;
            this.text = text;
        }

        public void add(Element element) {
            element.parent = this;
            this.elements.add(element);
        }

        public List<Element> getElements() {
            return this.elements;
        }

        public String getName() {
            return this.name;
        }

        public String getText() {
            return this.text;
        }

        public String getTitle() {
            return this.name;
        }

        public TemplateModel get(String key) {
            return null;
        }

        @Override
        public TemplateNodeModel getParentNode() throws TemplateModelException {
            return this.parent;
        }

        @Override
        public TemplateSequenceModel getChildNodes() throws TemplateModelException {
            // TODO Auto-generated method stub
            return new SimpleSequence(this.elements, cfg.getObjectWrapper());
        }

        @Override
        public String getNodeName() throws TemplateModelException {
            return this.name;
        }

        @Override
        public String getNodeType() throws TemplateModelException {
            return this.name;
        }

        @Override
        public String getNodeNamespace() throws TemplateModelException {
            return null;
        }
    }

    private static Configuration cfg;
    private static final String myTestTemplate = "<#recurse doc>\r\n" +
            "\r\n" +
            "<#macro book>\r\n" +
            "  Book element with title ${.node.title} \r\n" +
            "    <#recurse>\r\n" +
            "  End book\r\n" +
            "</#macro>\r\n" +
            "\r\n" +
            "<#macro title>\r\n" +
            "  Title element\r\n" +
            "</#macro>\r\n" +
            "\r\n" +
            "<#macro chapter>\r\n" +
            "  Chapter element with title: ${.node.title}\r\n" +
            "</#macro>";

    @BeforeClass
    public static void classInit() throws IOException {

        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("myTestTemplate", myTestTemplate);

        cfg = new Configuration(Configuration.VERSION_2_3_29);
        cfg.setTemplateLoader(stringTemplateLoader);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setWrapUncheckedExceptions(true);
        cfg.setFallbackOnNullLoopVariable(false);
    }

    @Test
    public void basicXmlTest() throws TemplateException, IOException {

        Element doc = new Element("doc");

        Element book = new Element("book");
        book.add(new Element("title", "Test Book"));
        doc.add(book);

        Element chapter1 = new Element("chapter");
        chapter1.add(new Element("title", "Ch1"));
        chapter1.add(new Element("para", "p1.1"));
        chapter1.add(new Element("para", "p1.2"));
        chapter1.add(new Element("para", "p1.3"));
        book.add(chapter1);

        Element chapter2 = new Element("chapter");
        chapter2.add(new Element("title", "Ch2"));
        chapter2.add(new Element("para", "p2.1"));
        chapter2.add(new Element("para", "p2.2"));
        chapter2.add(new Element("para", "p2.3"));
        book.add(chapter2);

        Map<String, Object> root = new HashMap<>();
        // Put string "user" into the root
        root.put("doc", doc);

        Template temp = cfg.getTemplate("myTestTemplate");

        Writer out = new OutputStreamWriter(System.out);
        temp.process(root, out);

    }
公共类FreemarkerXmlTests{
静态类元素实现TemplateNodeModel{
私有最终字符串名;
私有最终字符串文本;
私有元素父元素;
私有最终列表元素=新的ArrayList();
公共元素(字符串名称){
此(名称,空);
}
公共元素(字符串名称、字符串文本){
this.name=名称;
this.text=文本;
}
公共空添加(元素){
element.parent=this;
this.elements.add(element);
}
公共列表getElements(){
返回此元素;
}
公共字符串getName(){
返回此.name;
}
公共字符串getText(){
返回此.text;
}
公共字符串getTitle(){
返回此.name;
}
公共模板模型获取(字符串键){
返回null;
}
@凌驾
公共TemplateNodeModel getParentNode()引发TemplateModelException{
将此文件返回给父对象;
}
@凌驾
公共TemplateSequenceModel getChildNodes()引发TemplateModelException{
//TODO自动生成的方法存根
返回新的SimpleSequence(this.elements,cfg.getObjectWrapper());
}
@凌驾
公共字符串getNodeName()引发TemplateModelException{
返回此.name;
}
@凌驾
公共字符串getNodeType()引发TemplateModelException{
返回此.name;
}
@凌驾
公共字符串getNodeNamespace()引发TemplateModelException{
返回null;
}
}
私有静态配置;
私有静态最终字符串myTestTemplate=“\r\n”+
“\r\n”+
“\r\n”+
“标题为${.node.title}的Book元素\r\n”+
“\r\n”+
“结束书本\r\n”+
“\r\n”+
“\r\n”+
“\r\n”+
“标题元素\r\n”+
“\r\n”+
“\r\n”+
“\r\n”+
“标题为:${.node.title}的章节元素\r\n”+
"";
@课前
public static void classInit()引发IOException{
StringTemplateLoader StringTemplateLoader=新StringTemplateLoader();
stringTemplateLoader.PuttTemplate(“myTestTemplate”,myTestTemplate);
cfg=新配置(配置.版本2\u 3\u 29);
设置模板装载机(stringTemplateLoader);
设置默认编码(“UTF-8”);
setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW\u HANDLER);
setLogTemplateExceptions(false);
cfg.setwrapunchckedExceptions(真);
setFallbackOnNullLoopVariable(false);
}
@试验
public void basicXmlTest()引发TemplateException,IOException{
要素单据=新要素(“单据”);
要素账簿=新要素(“账簿”);
新增(新元素(“标题”、“测试书”);
文件添加(书籍);
要素第1章=新要素(“章节”);
第1章添加(新元素(“标题”、“Ch1”);
第1章添加(新元素(“第1.1段”);
第1章添加(新元素(“第1.2段”);
第1章添加(新元素(“第1.3段”);
书。增补(第1章);
要素第2章=新要素(“章节”);
第2章添加(新元素(“标题”,“Ch2”));
第2章添加(新元素(“第2.1款”);
第2章添加(新元素(“第2.2段”);
第2章添加(新元素(“第2.3段”);
图书增刊(第2章);
Map root=newhashmap();
//将字符串“user”放入根目录
root.put(“doc”,doc);
模板temp=cfg.getTemplate(“myTestTemplate”);
Writer out=新的OutputStreamWriter(System.out);
温度过程(根,输出);
}
有什么想法吗?

看看界面。您的对象必须实现它,或者必须(通过)将它们包装到实现它的工具中。然后,递归访问/
/
?父对象
/
?子对象
/等将与它们一起工作

下面是一个用于遍历JSON的实现示例:

使用上述内容的一些模板:

操作符开始,为此您需要实现
TemplateHashModel
(或其子接口,如
TemplateHashModelEx2
)。

查看该接口。您的对象必须实现该接口,或者必须(通过然后递归
/
#访问
/
?父项
/
?子项
/等将与它们一起工作

下面是一个用于遍历JSON的实现示例:

使用上述内容的一些模板:


对于
操作符,您需要实现
TemplateHashModel
(或其子接口,如
TemplateHashModelEx2
)。

在ddekany发布的示例的帮助下,我添加了以下内容:

  • 对元素类实现TemplateHashModel
  • 元素类和单元测试的一种方法:

  • 在ddekany发布的示例的帮助下,我添加了以下内容:

  • 对元素类实现TemplateHashModel
  • 元素类和单元测试的一种方法:
  • 非常有用的例子
        static class Element implements TemplateNodeModel, TemplateHashModel {
    
            @Override
            public TemplateModel get(String key) throws TemplateModelException {
    
                switch (key) {
                case "title":
                case "name":
                    return cfg.getObjectWrapper().wrap(this.name);
    
                default:
                    throw new TemplateModelException("unknown hash get: " + key);
                }
            }