精华内容
下载资源
问答
  • XML代码模板引擎从XML文档概述中生成类代码框架,该类代码框架可以包括C ++或Java(到目前为止)中的相等性和日志记录方法。 它还创建了与Doxygen兼容的注释,并且具有非常简单的插件系统。
  • c# 模板引擎TemplateEngine 源码生成

    热门讨论 2009-10-24 08:39:36
    VMoom.TemplateEngine 是一个用 C# 编写的 .Net 类库,它用于从模板源 代码根据指定的参数生成文本。它可以用于网页制作、电子邮件模板、xml 代 码生成器、源代码生成器等等。
  • 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!...
  • Sempare模板引擎 版权所有(c)2019-2021 联系方式: 许可证: 或 开源: : 内容 要求 安装:GetIt 安装方式:Delphinus ... Sempare.Template; type TInformation = record name: string;
  • Template Engine-开源

    2021-05-15 01:05:00
    用于HTML / XML /文本模板的快速模板处理器。 无需学习模板语言或新标签。 模板可以是有效HTML / XML。 易于使用且功能强大!
  • Toxic Template Engine-开源

    2021-05-14 15:25:16
    有毒是一种通用的模板引擎,即,它会以不同的模板输入格式(例如,文本模板,XML模板或SQL模板)生成文本输出。 新的模板格式可以很容易地集成甚至混合在一起。
  • JSP template engine-开源

    2021-04-19 08:35:07
    一个JSP模板引擎,使开发人员能够编写JSP电子邮件,JSP XML文件,静态(预编译)HTML文件和任何其他文本格式。 语法几乎与JSP相同,但是不需要在Web服务器中运行。
  • 将任何模板转换为可执行PHP代码,并将其存储以供以后使用,例如HTML,RSS或XML。 处理标量和数组值,块,循环,控制结构,包括扩展/功能,过滤器,i18n内置功能,缓存
  • Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。  当Velocity应用于web开发时,界面设计人员可以和java程序开发...

    Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。 

    当Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只 关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来,这样为web站点的长期维护提 供了便利,同时也为我们在JSP和PHP之外又提供了一种可选的方案。 

    Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQL和PostScript、XML,它也可以被当 作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务(template service)。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。

    Velocity的Eclipse插件的Update地址是:http://propsorter.sourceforge.net/veloeclipse

    Velocity 的 JavaDoc: http://www.ostools.net/apidocs/apidoc?api=velocity-1.7

    展开全文
  • Template engines 1. Introduction Groovy supports multiple ways to generate text dynamically includingGStrings,printfandMarkupBuilderjust to name a few. In addition to these, there is a dedicate...

    Template engines

    1. Introduction

    Groovy supports multiple ways to generate text dynamically including GStringsprintf and MarkupBuilder just to name a few. In addition to these, there is a dedicated template framework which is well-suited to applications where the text to be generated follows the form of a static template.

    2. Template framework

    The template framework in Groovy consists of a TemplateEngine abstract base class that engines must implement and a Template interface that the resulting templates they generate must implement.

    Included with Groovy are several template engines:

    • SimpleTemplateEngine - for basic templates

    • StreamingTemplateEngine - functionally equivalent to SimpleTemplateEngine, but can handle strings larger than 64k

    • GStringTemplateEngine - stores the template as writeable closures (useful for streaming scenarios)

    • XmlTemplateEngine - works well when the template and output are valid XML

    • MarkupTemplateEngine - a very complete, optimized, template engine

    3. SimpleTemplateEngine

    Shown here is the SimpleTemplateEngine that allows you to use JSP-like scriptlets (see example below), script, and EL expressions in your template in order to generate parametrized text. Here is an example of using the system:

    def text = 'Dear "$firstname $lastname",\nSo nice to meet you in <% print city %>.\nSee you in ${month},\n${signed}'
    
    def binding = ["firstname":"Sam", "lastname":"Pullara", "city":"San Francisco", "month":"December", "signed":"Groovy-Dev"]
    
    def engine = new groovy.text.SimpleTemplateEngine()
    def template = engine.createTemplate(text).make(binding)
    
    def result = 'Dear "Sam Pullara",\nSo nice to meet you in San Francisco.\nSee you in December,\nGroovy-Dev'
    
    assert result == template.toString()

    While it is generally not deemed good practice to mix processing logic in your template (or view), sometimes very simple logic can be useful. E.g. in the example above, we could change this:

    $firstname

    to this (assuming we have set up a static import for capitalize inside the template):

    ${firstname.capitalize()}

    or this:

    <% print city %>

    to this:

    <% print city == "New York" ? "The Big Apple" : city %>

    3.1. Advanced Usage Note

    If you happen to be embedding your template directly in your script (as we did above) you have to be careful about backslash escaping. Because the template string itself will be parsed by Groovy before it is passed to the templating framework, you have to escape any backslashes inside GString expressions or scriptlet 'code' that are entered as part of a Groovy program. E.g. if we wanted quotes around The Big Apple above, we would use:

    <% print city == "New York" ? "\\"The Big Apple\\"" : city %>

    Similarly, if we wanted a newline, we would use:

    \\n

    in any GString expression or scriptlet 'code' that appears inside a Groovy script. A normal “\n” is fine within the static template text itself or if the entire template itself is in an external template file. Similarly, to represent an actual backslash in your text you would need

    \\\\

    in an external file or

    \\\\

    in any GString expression or scriptlet 'code'. (Note: the necessity to have this extra slash may go away in a future version of Groovy if we can find an easy way to support such a change.)

    4. StreamingTemplateEngine

    The StreamingTemplateEngine engine is functionally equivalent to the SimpleTemplateEngine, but creates the template using writable closures making it more scalable for large templates. Specifically this template engine can handle strings larger than 64k.

    It uses JSP style <% %> script and <%= %> expression syntax or GString style expressions. The variable 'out' is bound to the writer that the template is being written to.

    Frequently, the template source will be a file but here we show a simple example providing the template as a string:

    def text = '''\
    Dear <% out.print firstname %> ${lastname},
    
    We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> \
    to inform you that your paper entitled
    '$title' was ${ accepted ? 'accepted' : 'rejected' }.
    
    The conference committee.'''
    
    def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)
    
    def binding = [
        firstname : "Grace",
        lastname  : "Hopper",
        accepted  : true,
        title     : 'Groovy for COBOL programmers'
    ]
    
    String response = template.make(binding)
    
    assert response == '''Dear Grace Hopper,
    
    We are pleased to inform you that your paper entitled
    'Groovy for COBOL programmers' was accepted.
    
    The conference committee.'''

    5. GStringTemplateEngine

    As an example of using the GStringTemplateEngine, here is the example above done again (with a few changes to show some other options). First we will store the template in a file this time:

    test.template

    Dear "$firstname $lastname",
    So nice to meet you in <% out << (city == "New York" ? "\\"The Big Apple\\"" : city) %>.
    See you in ${month},
    ${signed}

    Note that we used out instead of print to support the streaming nature of GStringTemplateEngine. Because we have the template in a separate file, there is no need to escape the backslashes. Here is how we call it:

    def f = new File('test.template')
    def engine = new groovy.text.GStringTemplateEngine()
    def template = engine.createTemplate(f).make(binding)
    println template.toString()

    and here is the output:

    Dear "Sam Pullara",
    So nice to meet you in "The Big Apple".
    See you in December,
    Groovy-Dev

    6. XmlTemplateEngine

    XmlTemplateEngine for use in templating scenarios where both the template source and the expected output are intended to be XML. Templates may use the normal ${expression} and $variable notations to insert an arbitrary expression into the template. In addition, support is also provided for special tags: <gsp:scriptlet> (for inserting code fragments) and <gsp:expression> (for code fragments which produce output).

    Comments and processing instructions will be removed as part of processing and special XML characters such as <>" and ' will be escaped using the respective XML notation. The output will also be indented using standard XML pretty printing.

    The xmlns namespace definition for gsp: tags will be removed but other namespace definitions will be preserved (but may change to an equivalent position within the XML tree).

    Normally, the template source will be in a file but here is a simple example providing the XML template as a string:

    def binding = [firstname: 'Jochen', lastname: 'Theodorou', nickname: 'blackdrag', salutation: 'Dear']
    def engine = new groovy.text.XmlTemplateEngine()
    def text = '''\
        <document xmlns:gsp='http://groovy.codehaus.org/2005/gsp' xmlns:foo='baz' type='letter'>
            <gsp:scriptlet>def greeting = "${salutation}est"</gsp:scriptlet>
            <gsp:expression>greeting</gsp:expression>
            <foo:to>$firstname "$nickname" $lastname</foo:to>
            How are you today?
        </document>
    '''
    def template = engine.createTemplate(text).make(binding)
    println template.toString()

    This example will produce this output:

    <document type='letter'>
      Dearest
      <foo:to xmlns:foo='baz'>
        Jochen &quot;blackdrag&quot; Theodorou
      </foo:to>
      How are you today?
    </document>

    7. The MarkupTemplateEngine

    This template engine is a template engine primarily aimed at generating XML-like markup (XML, XHTML, HTML5, …​), but that can be used to generate any text based content. Unlike traditional template engines, this one relies on a DSL that uses the builder syntax. Here is a sample template:

    xmlDeclaration()
    cars {
       cars.each {
           car(make: it.make, model: it.model)
       }
    }

    If you feed it with the following model:

    model = [cars: [new Car(make: 'Peugeot', model: '508'), new Car(make: 'Toyota', model: 'Prius')]]

    It would be rendered as:

    <?xml version='1.0'?>
    <cars><car make='Peugeot' model='508'/><car make='Toyota' model='Prius'/></cars>

    The key features of this template engine are:

    • markup builder like syntax

    • templates are compiled into bytecode

    • fast rendering

    • optional type checking of the model

    • includes

    • internationalization support

    • fragments/layouts

    7.1. The template format

    7.1.1. Basics

    Templates consist of Groovy code. Let’s explore the first example more thoroughly:

    xmlDeclaration()                                
    cars {                                          
       cars.each {                                  
           car(make: it.make, model: it.model)      
       }                                            
    }
     renders the XML declaration string.
     opens a cars tag
     cars is a variable found in the template model, which is a list of Car instances
     for each item, we create a car tag with the attributes from the Car instance
     closes the cars tag

    As you can see, regular Groovy code can be used in the template. Here, we are calling each on a list (retrieved from the model), allowing us to render one car tag per entry.

    In a similar fashion, rendering HTML code is as simple as this:

    yieldUnescaped '<!DOCTYPE html>'                                                    
    html(lang:'en') {                                                                   
        head {                                                                          
            meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')      
            title('My page')                                                            
        }                                                                               
        body {                                                                          
            p('This is an example of HTML contents')                                    
        }                                                                               
    }                                                                                   
     renders the HTML doctype special tag
     opens the html tag with an attribute
     opens the head tag
     renders a meta tag with one http-equiv attribute
     renders the title tag
     closes the head tag
     opens the body tag
     renders a p tag
     closes the body tag
     closes the html tag

    The output is straightforward:

    <!DOCTYPE html><html lang='en'><head><meta http-equiv='"Content-Type" content="text/html; charset=utf-8"'/><title>My page</title></head><body><p>This is an example of HTML contents</p></body></html>
     With some configuration, you can have the output pretty printed, with newlines and indent automatically added.

    7.1.2. Support methods

    In the previous example, the doctype declaration was rendered using the yieldUnescaped method. We have also seen the xmlDeclaration method. The template engine provides several support methods that will help you render contents appropriately:

    MethodDescriptionExample

    yield

    Renders contents, but escapes it before rendering

    Template:

    yield 'Some text with <angle brackets>'

    Output:

    Some text with &lt;angle brackets&gt;

    yieldUnescaped

    Renders raw contents. The argument is rendered as is, without escaping.

    Template:

    yieldUnescaped 'Some text with <angle brackets>'

    Output:

    Some text with <angle brackets>

    xmlDeclaration

    Renders an XML declaration String. If the encoding is specified in the configuration, it is written in the declaration.

    Template:

    xmlDeclaration()

    Output:

    <?xml version='1.0'?>

    If TemplateConfiguration#getDeclarationEncoding is not null:

    Output:

    <?xml version='1.0' encoding='UTF-8'?>

    comment

    Renders raw contents inside an XML comment

    Template:

    comment 'This is <a href='http://docs.groovy-lang.org/latest/html/documentation/foo.html'>commented out</a>'

    Output:

    <!--This is <a href='http://docs.groovy-lang.org/latest/html/documentation/foo.html'>commented out</a>-->

    newLine

    Renders a new line. See also TemplateConfiguration#setAutoNewLine and TemplateConfiguration#setNewLineString.

    Template:

    p('text')
    newLine()
    p('text on new line')

    Output:

    <p>text</p>
    <p>text on new line</p>

    pi

    Renders an XML processing instruction.

    Template:

    pi("xml-stylesheet":[href:"mystyle.css", type:"text/css"])

    Output:

    <?xml-stylesheet href='mystyle.css' type='text/css'?>

    tryEscape

    Returns an escaped string for an object, if it is a String (or any type derived from CharSequence). Otherwise returns the object itself.

    Template:

    yieldUnescaped tryEscape('Some text with <angle brackets>')

    Output:

    Some text with &lt;angle brackets&gt;

    7.1.3. Includes

    The MarkupTemplateEngine supports inclusion of contents from another file. Included contents may be:

    • another template

    • raw contents

    • contents to be escaped

    Including another template can be done using:

    include template: 'other_template.tpl'

    Including a file as raw contents, without escaping it, can be done like this:

    include unescaped: 'raw.txt'

    Eventually, inclusion of text that should be escaped before rendering can be done using:

    include escaped: 'to_be_escaped.txt'

    Alternatively, you can use the following helper methods instead:

    • includeGroovy(<name>) to include another template

    • includeEscaped(<name>) to include another file with escaping

    • includeUnescaped(<name>) to include another file without escaping

    Calling those methods instead of the include xxx: syntax can be useful if the name of the file to be included is dynamic (stored in a variable for example). Files to be included (independently of their type, template or text) are found on classpath. This is one of the reasons why the MarkupTemplateEngine takes an optional ClassLoader as constructor argument (the other reason being that you can include code referencing other classes in a template).

    If you don’t want your templates to be on classpath, the MarkupTemplateEngine accepts a convenient constructor that lets you define the directory where templates are to be found.

    7.1.4. Fragments

    Fragments are nested templates. They can be used to provide improved composition in a single template. A fragment consists of a string, the inner template, and a model, used to render this template. Consider the following template:

    ul {
        pages.each {
            fragment "li(line)", line:it
        }
    }

    The fragment element creates a nested template, and renders it with a model which is specific to this template. Here, we have the li(line) fragment, where line is bound to it. Since it corresponds to the iteration of pages, we will generate a single li element for each page in our model:

    <ul><li>Page 1</li><li>Page 2</li></ul>

    Fragments are interesting to factorize template elements. They come at the price of the compilation of a fragment per template, and they cannot be externalized.

    7.1.5. Layouts

    Layouts, unlike fragments, refer to other templates. They can be used to compose templates and share common structures. This is often interesting if you have, for example, a common HTML page setup, and that you only want to replace the body. This can be done easily with a layout. First of all, you need to create a layout template:

    layout-main.tpl

    html {
        head {
            title(title)                
        }
        body {
            bodyContents()              
        }
    }
     the title variable (inside the title tag) is a layout variable
     the bodyContents call will render the body

    Then what you need is a template that includes the layout:

    layout 'layout-main.tpl',                                   
        title: 'Layout example',                                
        bodyContents: contents { p('This is the body') }        
     use the main-layout.tpl layout file
     set the title variable
     set the bodyContents

    As you can see, bodyContents will be rendered inside the layout, thanks to the bodyContents() call in the layout file. As a result, the template will be rendered as this:

    <html><head><title>Layout example</title></head><body><p>This is the body</p></body></html>

    The call to the contents method is used to tell the template engine that the block of code is in fact a specification of a template, instead of a helper function to be rendered directly. If you don’t add contents before your specification, then the contents would be rendered, but you would also see a random string generated, corresponding to the result value of the block.

    Layouts are a powerful way to share common elements across multiple templates, without having to rewrite everything or use includes.

    Layouts use, by default, a model which is independent from the model of the page where they are used. It is however possible to make them inherit from the parent model. Imagine that the model is defined like this:

    model = new HashMap<String,Object>();
    model.put('title','Title from main model');

    and the following template:

    layout 'layout-main.tpl', true,                             
        bodyContents: contents { p('This is the body') }
     note the use of true to enable model inheritance

    then it is not necessary to pass the title value to the layout as in the previous example. The result will be:

    <html><head><title>Title from main model</title></head><body><p>This is the body</p></body></html>

    But it is also possible to override a value from the parent model:

    layout 'layout-main.tpl', true,                             
        title: 'overridden title',                               
        bodyContents: contents { p('This is the body') }
     true means inherit from the parent model
     but title is overridden

    then the output will be:

    <html><head><title>overridden title</title></head><body><p>This is the body</p></body></html>

    7.2. Rendering contents

    7.2.1. Creation of a template engine

    On the server side, rendering templates require an instance of groovy.text.markup.MarkupTemplateEngine and a groovy.text.markup.TemplateConfiguration:

    TemplateConfiguration config = new TemplateConfiguration();         
    MarkupTemplateEngine engine = new MarkupTemplateEngine(config);     
    Template template = engine.createTemplate("p('test template')");    
    Map<String, Object> model = new HashMap<>();                        
    Writable output = template.make(model);                             
    output.writeTo(writer);                                             
     creates a template configuration
     creates a template engine with this configuration
     creates a template instance from a String
     creates a model to be used in the template
     bind the model to the template instance
     render output

    There are several possible options to parse templates:

    • from a String, using createTemplate(String)

    • from a Reader, using createTemplate(Reader)

    • from a URL, using createTemplate(URL)

    • given a template name, using createTemplateByPath(String)

    The last version should in general be preferred:

    Template template = engine.createTemplateByPath("main.tpl");
    Writable output = template.make(model);
    output.writeTo(writer);

    7.2.2. Configuration options

    The behavior of the engine can be tweaked with several configuration options accessible through the TemplateConfiguration class:

    OptionDefault valueDescriptionExample

    declarationEncoding

    null

    Determines the value of the encoding to be written when xmlDeclaration is called. It does not influence the writer you are using as output.

    Template:

    xmlDeclaration()

    Output:

    <?xml version='1.0'?>

    If TemplateConfiguration#getDeclarationEncoding is not null:

    Output:

    <?xml version='1.0' encoding='UTF-8'?>

    expandEmptyElements

    false

    If true, empty tags are rendered in their expanded form.

    Template:

    p()

    Output:

    <p/>

    If expandEmptyElements is true:

    Output:

    <p></p>

    useDoubleQuotes

    false

    If true, use double quotes for attributes instead of simple quotes

    Template:

    tag(attr:'value')

    Output:

    <tag attr='value'/>

    If useDoubleQuotes is true:

    Output:

    <tag attr="value"/>

    newLineString

    System default (system property line.separator)

    Allows to choose what string is used when a new line is rendered

    Template:

    p('foo')
    newLine()
    p('baz')

    If newLineString='BAR':

    Output:

    <p>foo</p>BAR<p>baz</p>

    autoEscape

    false

    If true, variables from models are automatically escaped before rendering.

    See the auto escape section

    autoIndent

    false

    If true, performs automatic indentation after new lines

    See the auto formatting section

    autoIndentString

    four (4) spaces

    The string to be used as indent.

    See the auto formatting section

    autoNewLine

    false

    If true, performs automatic insertion of new lines

    See the auto formatting section

    baseTemplateClass

    groovy.text.markup.BaseTemplate

    Sets the super class of compiled templates. This can be used to provide application specific templates.

    See the custom templates section

    locale

    Default locale

    Sets the default locale for templates.

    See the internationalization section

     Once the template engine has been created, it is unsafe to change the configuration.

    7.2.3. Automatic formatting

    By default, the template engine will render output without any specific formatting. Some configuration options can improve the situation:

    • autoIndent is responsible for auto-indenting after a new line is inserted

    • autoNewLine is responsible for automatically inserting new lines based on the original formatting of the template source

    In general, it is recommended to set both autoIndent and autoNewLine to true if you want human-readable, pretty printed, output:

    config.setAutoNewLine(true);
    config.setAutoIndent(true);

    Using the following template:

    html {
        head {
            title('Title')
        }
    }

    The output will now be:

    <html>
        <head>
            <title>Title</title>
        </head>
    </html>

    We can slightly change the template so that the title instruction is found on the same line as the head one:

    html {
        head { title('Title')
        }
    }

    And the output will reflect that:

    <html>
        <head><title>Title</title>
        </head>
    </html>

    New lines are only inserted where curly braces for tags are found, and the insertion corresponds to where the nested content is found. This means that tags in the body of another tag will not trigger new lines unless they use curly braces themselves:

    html {
        head {
            meta(attr:'value')          
            title('Title')              
            newLine()                   
            meta(attr:'value2')         
        }
    }
     a new line is inserted because meta is not on the same line as head
     no new line is inserted, because we’re on the same depth as the previous tag
     we can force rendering of a new line by explicitly calling newLine
     and this tag will be rendered on a separate line

    This time, the output will be:

    <html>
        <head>
            <meta attr='value'/><title>Title</title>
            <meta attr='value2'/>
        </head>
    </html>

    By default, the renderer uses four(4) spaces as indent, but you can change it by setting the TemplateConfiguration#autoIndentString property.

    7.2.4. Automatic escaping

    By default, contents which is read from the model is rendered as is. If this contents comes from user input, it can be sensible, and you might want to escape it by default, for example to avoid XSS injection. For that, the template configuration provides an option which will automatically escape objects from the model, as long as they inherit from CharSequence (typically, `String`s).

    Let’s imagine the following setup:

    config.setAutoEscape(false);
    model = new HashMap<String,Object>();
    model.put("unsafeContents", "I am an <html> hacker.");

    and the following template:

    html {
        body {
            div(unsafeContents)
        }
    }

    Then you wouldn’t want the HTML from unsafeContents to be rendered as is, because of potential security issues:

    <html><body><div>I am an <html> hacker.</div></body></html>

    Automatic escaping will fix this:

    config.setAutoEscape(true);

    And now the output is properly escaped:

    <html><body><div>I am an &lt;html&gt; hacker.</div></body></html>

    Note that using automatic escaping doesn’t prevent you from including unescaped contents from the model. To do this, your template should then explicitly mention that a model variable should not be escaped by prefixing it with unescaped., like in this example:

    Explicit bypass of automatic escaping

    html {
        body {
            div(unescaped.unsafeContents)
        }
    }

    7.2.5. Common gotchas

    Strings containing markup

    Say that you want to generate a <p> tag which contains a string containing markup:

    p {
        yield "This is a "
        a(href:'target.html', "link")
        yield " to another page"
    }

    and generates:

    <p>This is a <a href='target.html'>link</a> to another page</p>

    Can’t this be written shorter? A naive alternative would be:

    p {
        yield "This is a ${a(href:'target.html', "link")} to another page"
    }

    but the result will not look as expected:

    <p><a href='target.html'>link</a>This is a  to another page</p>

    The reason is that the markup template engine is a streaming engine. In the original version, the first yield call generates a string which is streamed to the output, then the a link is generated and streamed, and then the last yield call is streamed, leading in an execution in order. But with the string version above, the order of execution is different:

    • the yield call requires an argument, a string

    • that arguments needs to be evaluated before the yield call is generated

    so evaluating the string leads to an execution of the a(href:…​) call before yield is itself called. This is not what you want to do. Instead, you want to generate a string which contains markup, which is then passed to the yield call. This can be done this way:

    p("This is a ${stringOf {a(href:'target.html', "link")}} to another page")

    Note the stringOf call, which basically tells the markup template engine that the underlying markup needs to be rendered separately and exported as a string. Note that for simple expressions, stringOf can be replaced by an alternate tag notation that starts with a dollar sign:

    p("This is a ${$a(href:'target.html', "link")} to another page")
     It is worth noting that using stringOf or the special $tag notation triggers the creation of a distinct string writer which is then used to render the markup. It is slower than using the version with calls to yield which perform direct streaming of the markup instead.

    7.2.6. Internationalization

    The template engine has native support for internationalization. For that, when you create the TemplateConfiguration, you can provide a Locale which is the default locale to be used for templates. Each template may have different versions, one for each locale. The name of the template makes the difference:

    • file.tpl: default template file

    • file_fr_FR.tpl: french version of the template

    • file_en_US.tpl: american english version of the template

    • …​

    When a template is rendered or included, then:

    • if the template name or include name explicitly sets a locale, the specific version is included, or the default version if not found

    • if the template name doesn’t include a locale, the version for the TemplateConfiguration locale is used, or the default version if not found

    For example, imagine the default locale is set to Locale.ENGLISH and that the main template includes:

    Use an explicit locale in include

    include template: 'locale_include_fr_FR.tpl'

    then the template is rendered using the specific template:

    Bypass the template configuration

    Texte en français

    Using an include without specifying a locale will make the template engine look for a template with the configured locale, and if not, fallback to the default, like here:

    Don’t use a locale in include

    include template: 'locale_include.tpl'

    Fallback to the default template

    Default text

    However, changing the default locale of the template engine to Locale.FRANCE will change the output, because the template engine will now look for a file with the fr_FR locale:

    Don’t fallback to the default template because a locale specific template was found

    Texte en français

    This strategy lets you translate your templates one by one, by relying on default templates, for which no locale is set in the file name.

    7.2.7. Custom template classes

    By default, templates created inherit the groovy.text.markup.BaseTemplate class. It may be interesting for an application to provide a different template class, for example to provide additional helper methods which are aware of the application, or customized rendering primitives (for HTML, for example).

    The template engine provides this ability by setting an alternative baseTemplateClass in the TemplateConfiguration:

    config.setBaseTemplateClass(MyTemplate.class);

    The custom base class has to extend BaseClass like in this example:

    public abstract class MyTemplate extends BaseTemplate {
        private List<Module> modules
        public MyTemplate(
                final MarkupTemplateEngine templateEngine,
                final Map model,
                final Map<String, String> modelTypes,
                final TemplateConfiguration configuration) {
            super(templateEngine, model, modelTypes, configuration)
        }
    
        List<Module> getModules() {
            return modules
        }
    
        void setModules(final List<Module> modules) {
            this.modules = modules
        }
    
        boolean hasModule(String name) {
            modules?.any { it.name == name }
        }
    }

    This example shows a class which provides an additional method named hasModule, which can then be used directly in the template:

    if (hasModule('foo')) {
        p 'Found module [foo]'
    } else {
        p 'Module [foo] not found'
    }

    7.3. Type checked templates

    7.3.1. Optional type checking

    Even if templates are not type checked, they are statically compiled. This means that once the templates are compiled, performance should be very good. For some applications, it might be good to make sure that templates are valid before they are actually rendered. This means failing template compilation, for example, if a method on a model variable doesn’t exist.

    The MarkupTemplateEngine provides such a facility. Templates can be optionally type checked. For that, the developer must provide additional information at template creation time, which is the types of the variables found in the model. Imagine a model exposing a list of pages, where a page is defined as:

    Page.groovy

    public class Page {
    
        Long id
        String title
        String body
    }

    Then a list of pages can be exposed in the model, like this:

    Page p = new Page();
    p.setTitle("Sample page");
    p.setBody("Page body");
    List<Page> pages = new LinkedList<>();
    pages.add(p);
    model = new HashMap<String,Object>();
    model.put("pages", pages);

    A template can use it easily:

    pages.each { page ->                    
        p("Page title: $page.title")        
        p(page.text)                        
    }
     iterate on pages from the model
     page.title is valid
     page.text is not (should be page.body)

    Without type checking, the compilation of the template succeeds, because the template engine doesn’t know about the model until a page is actually rendered. This means that the problem would only surface at runtime, once the page is rendered:

    Runtime error

    No such property: text

    In some situations, this can be complicated to sort out or even notice. By declaring the type of the pages to the template engine, we’re now capable of failing at compile time:

    modelTypes = new HashMap<String,String>();                                          
    modelTypes.put("pages", "List<Page>");                                              
    Template template = engine.createTypeCheckedModelTemplate("main.tpl", modelTypes)   
     create a map which will hold the model types
     declare the type of the pages variables (note the use of a string for the type)
     use createTypeCheckedModelTemplate instead of createTemplate

    This time, when the template is compiled at the last line, an error occurs:

    Template compilation time error

    [Static type checking] - No such property: text for class: Page

    This means that you don’t need to wait for the page to be rendered to see an error. The use of createTypeCheckedModelTemplate is mandatory.

    7.3.2. Alternative declaration of types

    Alternatively, if the developer is also the one who writes the templates, it is possible to declare the types of the expected variables directly in the template. In this case, even if you call createTemplate, it will be type checked:

    Inline declaration of types

    modelTypes = {                          
        List<Page> pages                    
    }
    
    pages.each { page ->
        p("Page title: $page.title")
        p(page.text)
    }
     types need to be declared in the modelTypes header
     declare one variable per object in the model

    7.3.3. Performance of type checked templates

    An additional interest of using type checked models is that performance should improve. By telling the type checker what are the expected types, you also let the compiler generate optimized code for that, so if you are looking for the best performance, consider using type checked templates.

    8. Other solutions

    Also, there are other templating solutions that can be used along with Groovy, such as FreeMarkerVelocityStringTemplate and others.

     
    展开全文
  • 背景 前段时间有一需求,需要动态修改...通过下文,将可以学到Spring Boot2.x+Thymeleaf的XML模式的使用,以及自定义Thymeleaf方言属性两个知识点;水平有限,若有误,欢迎各路英雄指正。 一、Thymeleaf的XML模...

    背景

    前段时间有一需求,需要动态修改xml模版的内容,但是网上能收集的资料多是关于thymeleaf的HTML使用方式;于是,在【科学上网】与自己的研究下,终于成功解决了这个需求。

    通过下文,将可以学到Spring Boot2.x+ThymeleafXML模式的使用,以及自定义Thymeleaf方言属性两个知识点;水平有限,若有误,欢迎各路英雄指正。

     

    一、Thymeleaf的XML模式

    1、创建spring boot工程

    使用IDEA创建spring boot工程,具体步骤参考其他资料。

     

    2、引入thymeleaf的依赖

    在pom.xml中,添加thymeleaf相关的依赖,如下所示

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
       <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.1.7.RELEASE</version>
          <relativePath/> <!-- lookup parent from repository -->
       </parent>
       <groupId>com.he</groupId>
       <artifactId>springboot-template</artifactId>
       <version>0.0.1-SNAPSHOT</version>
       <name>springboot-template</name>
       <description>Spring Boot2.x整合模板引擎技术</description>
    
       <properties>
          <java.version>1.8</java.version>
       </properties>
    
       <dependencies>
          <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
          </dependency>
          <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
    
    
          <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <optional>true</optional>
          </dependency>
          <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
          </dependency>
          <dependency>
             <groupId>io.springfox</groupId>
             <artifactId>springfox-swagger2</artifactId>
             <version>2.8.0</version>
          </dependency>
          <dependency>
             <groupId>io.springfox</groupId>
             <artifactId>springfox-swagger-ui</artifactId>
             <version>2.8.0</version>
          </dependency>
       </dependencies>
    
       <build>
          <plugins>
             <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
             </plugin>
          </plugins>
       </build>
    
    </project>

    3、在[application.properties]中,修改配置

    在此简单起见,我全部使用spring boot默认的配置,可以看到该文件内容是为空的

     

    4、新建Thymeleaf配置类,提供Bean

    Thymeleaf的XML模式,不像默认的HTML,需要额外提供两个Bean才可以成功运行;

    第一个,是[SpringResourceTemplateResolver],模版解析器;

    第二个,是[SpringTemplateEngine],Spring的模版引擎。

    • 新建类[ThymeleafConfig.java]如下
    package com.he.config;
    
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.thymeleaf.spring5.SpringTemplateEngine;
    import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
    import org.thymeleaf.templatemode.TemplateMode;
    
    
    /**
    * @date 2019/10/4
    * @des Thymeleaf配置
    */
    @Configuration
    public class ThymeleafConfig {
    
    
        @Bean
        SpringResourceTemplateResolver xmlTemplateResolver(ApplicationContext appCtx) {
            SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
    
            templateResolver.setApplicationContext(appCtx);
            templateResolver.setPrefix("classpath:/templates/");//指定模版前缀,即存放位置,默认是该地址
            templateResolver.setSuffix(".xml");//指定模版后缀
            templateResolver.setTemplateMode(TemplateMode.XML);//指定使用‘XML’模式
            templateResolver.setCharacterEncoding("UTF-8");//指定使用‘UTF-8’编码
            templateResolver.setCacheable(true);//开启缓存
    
            return templateResolver;
        }
    
        @Bean
        SpringTemplateEngine templateEngine(ApplicationContext appCtx) {
            SpringTemplateEngine templateEngine = new SpringTemplateEngine();
            templateEngine.setEnableSpringELCompiler(true);
            templateEngine.setTemplateResolver(xmlTemplateResolver(appCtx));
            return templateEngine;
        }
    
    }

     

    5、添加Thymeleaf模版

    在[resources-templates]中,新建我们的模版[person-test.xml],注意Thymeleaf语法的正确使用

    xml模版内容如下,十分简单,我们模拟动态的修改一个人的姓名国籍信息

    <?xml version="1.0" encoding="UTF-8"?>
    <persons>
        <person>
            <!--tag内容,使用strings.concat()拼接示例,读取map内容-->
            <fname th:text="${#strings.concat('firstname=',pinfo['lastname'])}"></fname>
            <!--tag属性,需设置多个属性时使用','隔开;或直接使用th:value的方式,
            但是当属性值为空时将不显示,若要显示则需要自定义属性方言-->
            <lname th:attr="lastname=${pinfo['firstname']}"></lname>
            <!--thymeleaf的其他具体语法参考官网-->
            <country th:text="${pinfo['country']}"></country>
        </person>
    </persons>

     

    6、在controller中编写测试示例

    在此,我引入swagger2,方便调试,具体引入参考代码,这里不再描述

    新建[TestController.java],编写我们的测试示例,如下所示

    package com.he.controller;
    
    
    import com.he.entity.*;
    import io.swagger.annotations.ApiImplicitParam;
    import io.swagger.annotations.ApiImplicitParams;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.thymeleaf.context.Context;
    import org.thymeleaf.spring5.SpringTemplateEngine;
    
    import java.util.*;
    
    
    /**
    * @date 2019/10/4
    * @des thymeleaf模版
    */
    @Slf4j
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
    
        @Autowired
        SpringTemplateEngine springTemplateEngine;
    
    
        @ApiOperation(value = "Thymeleaf模版的XML模式", notes = "XML模版")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "lastname", value = "姓氏"),
                @ApiImplicitParam(name = "firstname", value = "名字"),
                @ApiImplicitParam(name = "country", value = "国籍")
        })
        @GetMapping(value = "/test1", produces = {MediaType.APPLICATION_XML_VALUE})//produces改为XML
        public String test1(@RequestParam String lastname, @RequestParam String firstname, @RequestParam String country) {
            Map<String, String> pinfo = new HashMap<>();
            Context context = new Context();
            context.setVariable("pinfo", pinfo);
            pinfo.put("lastname", lastname);
            pinfo.put("firstname", firstname);
            pinfo.put("country", country);
    
    
            log.info("---pinfo:{}", pinfo);
            String content = springTemplateEngine.process("person-test", context);
            log.info("---xml:\n{}", content);
            return content;
        }
    }

     

    在swagger-ui.html输入测试参数后,日志打印如下

    2019-10-12 16:38:23.898  INFO 12504 --- [nio-8080-exec-7] com.he.controller.TestController         : ---pinfo:{country=china, firstname=haha, lastname=ho}
    2019-10-12 16:38:24.063  INFO 12504 --- [nio-8080-exec-7] com.he.controller.TestController         : ---xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <persons>
        <person>
            <!--tag内容,使用strings.concat()拼接示例,读取map内容-->
            <fname>firstname=ho</fname>
            <!--tag属性,需设置多个属性时使用','隔开;或直接使用th:value的方式,
            但是当属性值为空时将不显示,若要显示则需要自定义属性方言-->
            <lname lastname="haha"></lname>
            <!--thymeleaf的其他具体语法参考官网-->
            <country>china</country>
        </person>
    </persons>

    页面返回如下

    可见XML的内容被修改了,到此Thymeleaf的XML模式正式实现。

     

     

    二、Thymeleaf自定义方言属性

    接下来看下Thymeleaf更加高级一点的用法,即自定义标签,自定义方言属性,在此我只介绍[自定义方言属性],并且是基于XML模式下,在HTML文件中引用是一样的,只需把模式切换回HTML即可

    1、背景描述

    假设我们需要通过SQL查询某一个数据库系统,但是这个SQL是通过xml协议实现的。我们需要动态的修改这个SQL,也就是根据具体的查询语句,动态改变XML的内容。但是,我在改变tag的标签属性时,遇到“属性值为空时,该属性将不被显示”的问题,就是当使用[th:attr]或[th:xxx]这种方式设置属性时,若attr-value为空,将不可见,这对于协议是不允许的。所以只能自定义方言实现这一需求。

    在这里,我们将定义我们自己的属性[zdy:attr]

     

    2、使用thymeleaf改造XML模版

    原xml协议如下:

    <?xml version="1.0" encoding="utf-8"?>
    <condition>
        <!--查询字段-->
        <select>
            <column func=" " name=" c1" nickname=" C1" comments=" 字段1"/>
            <column func=" " name=" c2" nickname=" C2" comments=" 字段2"/>
        </select>
        <!--查询条件-->
        <where>
            <cd func="count($VALUE)" column="c1" compare="=" value="" relation=""/>
            <cd func="" column="c2" compare="=" value="" relation="and"/>
        </where>
    </condition>

    改造后,如下:

    <?xml version="1.0" encoding="utf-8"?>
    <condition>
        <!--查询字段-->
        <select>
            <column th:each="column:${condition.select.columns}"
                    zdy:attr="${#strings.concat('func=',column.func)};
                            ${#strings.concat('name=',column.name)};
                            ${#strings.concat('nickname=',column.nickname)};
                            ${#strings.concat('comments=',column.comments)}"/>
        </select>
        <!--查询条件-->
        <where th:if="${condition.where}">
            <cd th:each="cd:${condition.where.cds}"
                zdy:attr="${#strings.concat('func=',cd.func)};
                        ${#strings.concat('column=',cd.column)};
                        ${#strings.concat('compare=',cd.compare)};
                        ${#strings.concat('value=',cd.value)};
                        ${#strings.concat('relation=',cd.relation)}"/>
        </where>
    </condition>

     

    3、定义对应的实体类

    实体类与xml的tag分别对应起来,有5个,分别如下

    package com.he.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    import java.io.Serializable;
    
    /**
    * @date 2019/10/4
    * @des XML模版<condition>标签
    *
    */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Condition implements Serializable {
    
    
        /**
         * 查询字段,非空
         */
        private Select select = new Select();
    
        /**
         * 查询条件,可为空
         */
        private Where where;
    }
    package com.he.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    import java.io.Serializable;
    import java.util.List;
    
    
    /**
    * @date 2019/10/4
    * @des XML模版<condition>里的<select>
    */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Select implements Serializable{
    
    
        /**
         * 表示单个或多个字段,动态添加
         */
        private List<Column> columns;
    }
    package com.he.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    import java.io.Serializable;
    
    
    /**
    * @date 2019/10/4
    * @des XML模版<condition>里的<select>里的<column>
    */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Column implements Serializable {
    
        //<column func="count($COLUMN)" name=" c3" nickname=" C3" comments=" 字段3"/>
    
        private String func = "";
    
        private String name = "";
    
        private String nickname = "";
    
        private String comments = "";
    
    }
    package com.he.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    import java.io.Serializable;
    import java.util.List;
    
    
    /**
    * @date 2019/10/4
    * @des XML模版<condition>里的<where>
    */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Where implements Serializable{
    
        /**
         * cd标签:单条件,动态添加
         */
        private List<Cd> cds;
    }
    package com.he.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    import java.io.Serializable;
    
    
    /**
    * @date 2019/10/4
    * @des XML模版<condition>里的<where>里的cd标签
    */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Cd implements Serializable {
    
        private String func = "";
    
        private String column = "";
    
        private String compare = "";
    
        private String value = "";
    
        private String relation = "";
    }

     

    4、定义Thymeleaf属性处理器

    自定义属性处理器,需要继承[AbstractAttributeTagProcessor]类,代码如下

    package com.he.config;
    
    
    import org.thymeleaf.IEngineConfiguration;
    import org.thymeleaf.context.ITemplateContext;
    import org.thymeleaf.engine.AttributeName;
    import org.thymeleaf.model.IProcessableElementTag;
    import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
    import org.thymeleaf.processor.element.IElementTagStructureHandler;
    import org.thymeleaf.standard.expression.IStandardExpression;
    import org.thymeleaf.standard.expression.IStandardExpressionParser;
    import org.thymeleaf.standard.expression.StandardExpressions;
    import org.thymeleaf.templatemode.TemplateMode;
    
    
    /**
    * @date 2019/10/4
    * @des 自定义thymeleaf属性处理器
    */
    public class CustomAttrProcessor extends AbstractAttributeTagProcessor {
    
        private static final String ATTR_NAME = "attr";//自定义属性名,即(:)后面的名称,与前缀组合后是(zdy:attr)
        private static final int PRECEDENCE = 10000;//优先级
    
        public CustomAttrProcessor(final String dialectPrefix) {
            super(
                    TemplateMode.XML,               // This processor will apply only to XML mode
                    dialectPrefix,                  // Prefix to be applied to name for matching
                    null,              // No tag name: match any tag name
                    false,        // No prefix to be applied to tag name
                    ATTR_NAME,                      // Name of the attribute that will be matched
                    true,        // Apply dialect prefix to attribute name
                    PRECEDENCE,                     // Precedence (inside dialect's precedence)
                    true);          // Remove the matched attribute afterwards
        }
    
    
        @Override
        protected void doProcess(
                final ITemplateContext context, final IProcessableElementTag tag,
                final AttributeName attributeName, final String attributeValue,
                final IElementTagStructureHandler structureHandler) {
    
            /*
             * In order to evaluate the attribute value as a Thymeleaf Standard Expression,
             * we first obtain the parser, then use it for parsing the attribute value into
             * an expression object, and finally execute this expression object.
             */
            final IEngineConfiguration configuration = context.getConfiguration();
    
            /*
             * Obtain the Thymeleaf Standard Expression parser
             */
            final IStandardExpressionParser parser =
                    StandardExpressions.getExpressionParser(configuration);
    
            /**
             * 根据“;”拆分属性组合(同一属性不允许多次出现在同一element中,所以使用‘;’进行属性组合)
             */
            System.out.println("--自定义thymeleaf属性值: " + attributeValue);
            String[] attrArray = attributeValue.split(";");
    
            if (attrArray != null && attrArray.length > 0) {
                //遍历每个属性,设置单个属性值
                for (int i = 0; i < attrArray.length; i++) {
                    //解析单个属性
                    String attr = attrArray[i];
                    /**
                     * Parse the attribute value as a Thymeleaf Standard Expression
                     */
                    final IStandardExpression expression =
                            parser.parseExpression(context, attr);
    
                    /**
                     * Execute the expression just parsed
                     */
                    final String tagAttr = (String) expression.execute(context);
    
                    //根据“=”拆分属性name和value
                    String[] tagAttrArray = tagAttr.split("=");
                    String attrName = tagAttrArray[0];
                    String attrValue = "";
                    if (tagAttrArray.length > 1) {
                        attrValue = tagAttrArray[1];
                    }
                    //设置属性
                    structureHandler.setAttribute(attrName, attrValue);
                }
            }
        }
    }

    在doProcess()方法里,根据自定义的属性,读取属性值,然后进行拆分出一个个单独的属性,使用structureHandler.setAttribute()重新给tag设置属性,从而达到修改属性的目的。

     

    5、定义Thymeleaf属性方言

    完成了第4步后,还需要自定义属性方言,并添加到模版引擎中。新建CustomAttrDialect.java继承AbstractProcessorDialect

    package com.he.config;
    
    import org.thymeleaf.dialect.AbstractProcessorDialect;
    import org.thymeleaf.processor.IProcessor;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
    * @date 2019/10/4
    * @des 自定义thymeleaf属性方言
    */
    public class CustomAttrDialect extends AbstractProcessorDialect {
    
        private static final String DIALECT_NAME = "custom";        // 方言名称
        private static final String PREFIX = "zdy";                 // 方言前缀,zdy(自定义) ,使用格式(zdy:*)
        public static final int PROCESSOR_PRECEDENCE = 1000;        // 方言优先级
    
        public CustomAttrDialect() {
            super(DIALECT_NAME, PREFIX, PROCESSOR_PRECEDENCE);
        }
    
        /*
         * 初始化方言处理器
         *
         */
        public Set<IProcessor> getProcessors(final String dialectPrefix) {
            final Set<IProcessor> processors = new HashSet<>();
            processors.add(new CustomAttrProcessor(dialectPrefix));//添加自定义的属性处理器
            return processors;
        }
    }

    然后,把它添加到模版引擎中,如下

    @Bean
    SpringTemplateEngine templateEngine(ApplicationContext appCtx) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setEnableSpringELCompiler(true);
        templateEngine.setTemplateResolver(xmlTemplateResolver(appCtx));
        templateEngine.addDialect(new CustomAttrDialect());//自定义方言
        return templateEngine;
    }

     

    6、在controller中添加测试方法

    简单测试起见,我们把SQL的参数写死,整个类如下

    package com.he.controller;
    
    
    import com.he.entity.*;
    import io.swagger.annotations.ApiImplicitParam;
    import io.swagger.annotations.ApiImplicitParams;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.thymeleaf.context.Context;
    import org.thymeleaf.spring5.SpringTemplateEngine;
    
    
    import java.util.*;
    
    
    
    
    /**
    * @date 2019/10/4
    * @des thymeleaf模版
    */
    @Slf4j
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @Autowired
        SpringTemplateEngine springTemplateEngine;
    
        @ApiOperation(value = "Thymeleaf模版的XML模式", notes = "XML模版")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "lastname", value = "姓氏"),
                @ApiImplicitParam(name = "firstname", value = "名字"),
                @ApiImplicitParam(name = "country", value = "国籍")
        })
        @GetMapping(value = "/test1", produces = {MediaType.APPLICATION_XML_VALUE})//produces改为XML
        public String test1(@RequestParam String lastname, @RequestParam String firstname, @RequestParam String country) {
            Map<String, String> pinfo = new HashMap<>();
            Context context = new Context();
            context.setVariable("pinfo", pinfo);
            pinfo.put("lastname", lastname);
            pinfo.put("firstname", firstname);
            pinfo.put("country", country);
    
            log.info("---pinfo:{}", pinfo);
            String content = springTemplateEngine.process("person-test", context);
            log.info("---xml:\n{}", content);
            return content;
        }
    
    
        @ApiOperation(value = "Thymeleaf动态修改XML", notes = "可指定参数动态修改XML")
        @GetMapping(value = "/test2", produces = {MediaType.APPLICATION_XML_VALUE})//produces改为XML
        public String test2() {
            Context context = new Context();
            
            //<condition>标签
            Condition condition = new Condition();
            condition = testSql();
            context.setVariable("condition", condition);
    
    
            String content = springTemplateEngine.process("xml-protocol", context);
            log.info("Thymeleaf dynamic---xml:\n{}", content);
            return content;
        }
    
    
        /**
         * ‘select count(c1) as xxx,c2 from tb where ...’
         *
         * @return Condition
         */
        private Condition testSql() {
            Condition condition = new Condition();
    
            //select字段
            List<Column> columns = new ArrayList<>();
            Column c1 = new Column();
            c1.setFunc("count(id)");
            c1.setNickname("id_count");
    
            Column c2 = new Column();
            c2.setName("id");
    
            columns.add(c1);
            columns.add(c2);
            condition.getSelect().setColumns(columns);
    
    
            //where条件
            List<Cd> cds = new ArrayList<>();
            Cd cd1 = new Cd();
            cd1.setColumn("name");
            cd1.setCompare("=");
            cd1.setValue("hehe");
    
            Cd cd2 = new Cd();
            cd2.setColumn("age");
            cd2.setCompare("=");
            cd2.setValue("18");
            cd2.setRelation("and");
    
            cds.add(cd1);
            cds.add(cd2);
            Where where = new Where(cds);
            condition.setWhere(where);
    
            return condition;
        }
    }

    在swagger-ui.html调用测试方法后,页面返回内容如下

    日志打印如下

    --自定义thymeleaf属性值: ${#strings.concat('func=',column.func)};
                            ${#strings.concat('name=',column.name)};
                            ${#strings.concat('nickname=',column.nickname)};
                            ${#strings.concat('comments=',column.comments)}
    --自定义thymeleaf属性值: ${#strings.concat('func=',column.func)};
                            ${#strings.concat('name=',column.name)};
                            ${#strings.concat('nickname=',column.nickname)};
                            ${#strings.concat('comments=',column.comments)}
    --自定义thymeleaf属性值: ${#strings.concat('func=',cd.func)};
                        ${#strings.concat('column=',cd.column)};
                        ${#strings.concat('compare=',cd.compare)};
                        ${#strings.concat('value=',cd.value)};
                        ${#strings.concat('relation=',cd.relation)}
    --自定义thymeleaf属性值: ${#strings.concat('func=',cd.func)};
                        ${#strings.concat('column=',cd.column)};
                        ${#strings.concat('compare=',cd.compare)};
                        ${#strings.concat('value=',cd.value)};
                        ${#strings.concat('relation=',cd.relation)}
    2019-10-12 17:41:39.288  INFO 18976 --- [nio-8080-exec-8] com.he.controller.TestController         : Thymeleaf dynamic---xml:
    <?xml version="1.0" encoding="utf-8"?>
    <condition>
        <!--查询字段-->
        <select>
            <column func="count(id)" name="" nickname="id_count" comments=""/>
            <column func="" name="id" nickname="" comments=""/>
        </select>
        <!--查询条件-->
        <where>
            <cd func="" column="name" compare="" value="hehe" relation=""/>
            <cd func="" column="age" compare="" value="18" relation="and"/>
        </where>
    </condition>

    可见,测试成功!

    至此,Thymeleaf的XML模式,自定义方言属性已介绍完毕,后面将介绍另一模版引擎Freemarker的XML模式使用方式。

     

    全文代码链接:

    https://gitee.com/he-running/springboot-template.git

     

     

     

     

     

    展开全文
  • kajiki:具有类似Genshi语法和Jinja样式块的基于Python XML的模板引擎
  • 强大的Java模板引擎,非常适合构建HTML或XML文档。 块也可以处理许多其他需求和情况。 标记内过滤器和默认值,每个文件多个摘要,分层主题,宏,条件包括,本地化等。
  • 介绍TemplateEngine

    万次阅读 2007-12-08 12:46:00
    由于分析工作实在简单, TemplateEngine的分析性能是非常高的. 而这4个符号的意义分别是: '#' 指令符号. #后必须跟着一个指令. 这些指令有if,foreach,等等.. 写法是 {# 指令 指令参数} 文章后面会说明常用的...

    ***************图书推荐*************************************************************************************


    21.7 元

    26.9 元

    38.3 元

    23.9 元

    71.3 元

    16.6 元

    *********************************************************************************************************************

    来源:Lostinet  http://www.cnblogs.com/Lostinet/archive/2007/12/07/987122.html

    TemplateEngine它不是一个产品的名字.它只是我的MVC框架中的一个子部分.

    这个引擎负责把对象数据呈现成文本数据.

    它有一些特征:

    1. 指令简单 - 内置不超过10个常用的指令.
    2. 基于XPath - 直接使用XPath查找对象,并且可以使用XPath强大的语法和函数.
    3. 容易扩展 - 无论是指令还是XPath函数都可以自定义.
    4. 解析超快 - 基于其语法特点,解析模板结构非常快.

    它使用了一个叫 LateBindingNavigator 的工具类, 用于支持使用XPath去查询对象的属性.

    使用XPath是个优点 , 同时也是个缺点. 因为开发人员必须熟悉XML,并且学习XPath知识. (例如曾经使用过XSLT)

    下面介绍一下它的基础语法:

    就像ASP的<%%>类似 ,
    TemplateEngine选择了使用'{'和'}'作为模板指令的开始和结束.
    而'{'之后,必须接一个符号'#','=','^','~'.

    例如:
    1.
    <img src="{~/images/logo.gif}"/>
    2.
    <title>{=/Controller/Title}</title>
    3.
    {# include Includes/Bottom.htm }
    4.
    <div>{^ MyService:HtmlEncodeWithBR(Content) }</div>

    如果'{'后面不接以上列出的符号 , 那么它就不是TemplateEngine的指令块.
    这个是为了防止常规使用{}的情况.
    例如以下HTML代码不会和TemplateEngine有冲突:
    <script>
    function window_onload()
    {
     try{$("usernamebox").focus()}catch(x){}
    }
    </script>

    使用{#...},{=...},{^...},{~...}的设计,可以很方便TemplateEngine的Parser的分析工作.
    因为Parser可以很简单地查找出'{',然后分析下一个字符是否符合上面列出的符号,然后再找出'}'就OK了.
    由于分析工作实在简单, TemplateEngine的分析性能是非常高的.

    而这4个符号的意义分别是:

    '#' 指令符号. #后必须跟着一个指令. 这些指令有if,foreach,等等..
    写法是 {# 指令 指令参数} 文章后面会说明常用的指令

    '~' 根目录符号. 用于生成URL. 例如如果网站的目录不是/,而是IIS下的子目录,例如/MyApp,
    那么 <img src="{~/images/logo.gif}"/> 就会生成 <img src='/MyApp/images/logo.gif"/>

    '=' 输出符号. 用于直接输出一个值 这个值是HtmlEncode后再输出的.
    写法是 {=XPath语句} 例如 {=/HttpContext/Request/Url/PathAndQuery}

    '^' 输出符号. 直接不经过HtmlEncode就输出原始的值. 这个用于输出HTML的内容的.
    例如 {^MyService:GenerateUserLinkHTML(/Controller/Article/User) }


    另外 {..} 结构还可以被注释起来 , 例如:
    <ul>
    <!--{#foreach /Controller/BlogItems/Item}-->
     <li><a href='{~/BlogArticle.Aspx?ArticleId}={=ArticleId}'>{=Title}</a></li>
    <!--{#end}-->
    </ul>
    实际指令仍然会运作, 不过注释起来后, '<!--'和'-->' 就不会输出了.
    这样的好处是如果切换到设计视图,这些指令就会隐藏起来.不会太影响设计的效果.
    通常这个用在 if,else,elseif,foreach,select,end 指令上. 这些指令属于block指令.


    如果要使用TemplateEngine,可以使用以下的C#代码:

    //初始化
    string file=Server.MapPath("myview.htm");
    //包含了用户自定义的扩展 , 例如自定义的#指令
    MyTemplateService service=new MyTemplateService();
    //把一个类的静态方法变成XPath里的 MyService:Xxxx() 函数
    service.SetExtension("MyService",typeof(MyService));
    //把一个对象的非静态方法变成XPath里的 Controller:Xxxx() 函数
    service.SetExtension("Controller",this);
    ITemplateTransform transform=TemplateDocument.CreateTransform(file,null,service);
    transform.Init();//通常这个在OnInit的时候做.

    //输出数据
    Dictionary<string, object> roots=new Dictionary<string, object>();
    //把对象变成XPath下的 /HttpContext
    roots.Add("HttpContext",Context);
    roots.Add("Controller",this);
    //...更多的根对象
    LateBindingNavigator navigator=new LateBindingNavigator(roots);
    //把根对象传入Execute方法中,输出到writer去.
    transform.Execute(writer,navigator);


    就这样,视图文件,自定义方法,自定义的数据,就会汇集在一块, 在Execute的过程中, 生成结果,写进 writer 去.

    roots.Add("HttpContext",Context);
    roots.Add("Controller",this);
    这个非常重要. 因为它定义了数据的根. 在myview.htm里 , 就可随时使用它们: 例如
    <table>
     <thead>
     <tr>
      <td>Key</td><td>Value</td>
     </tr>
     </thead>
     <tbody>
     <!--{#foreach /HttpContext/Request/Params/Item}-->
     <tr>
      <td>{=Key}</td><td>{=Value}</td>
     </tr>
     <!--{#end}-->
     </tbody>
    </table>
    如果Controller中自定义了某些属性,
    public string Message
    {
     get
     {
      return _message;
     }
    }
    也可以直接调用:
    <div class="message">
    {=/Controller/Message}
    </div>

     

    XPath语法:

    XPath的具体语法 , 可以参考XMLSDK, 例如去搜索引擎查找 'xmlsdk.chm' , 就会有相关的XPATH介绍.
    用过XSL的人对XPATH会比较熟悉的. 实际上TemplateEngine的指令也比较接近XSL.

    LateBindingNavigator中定义了一些元素的规则.

    例如
    Property规则:
    使用一个属性名称作为NodeName,可以返回该属性的指,例如
    <!--{#if /HttpContext/User/Identity/IsAuthenticated}-->
    你已经登陆!
    <!--{#end}-->
    相当于HttpContext.User.Identity.IsAuthenticated

    Item规则
    当对象是ICollection的时候,自动生成Item用于检索每一个元素.
    例如常规
    {=/HttpContext/Request/QueryString/Count}可得到有多少个URL参数
    而这个
    <!--{#foreach /HttpContext/Request/QueryString/Item}-->
    {#comment 当前'.'为string }
    {=.}
    <!--{#end}-->
    则循环ICollection的一项

    @Key规则
    使用@Key可以直接执行this["Key"]返回对象,
    例如
    {=/HttpContext/Request/QueryString/@PageSize}


    可扩展的规则:
    例如
    {=/HttpContext/GetHashCode}
    GetHashCode它原本是个方法,但是经过LateBindingNavigator变成一个扩展的属性.
    开发人员可以通过实现ILateBindingNavigatorProvider来自定义扩展的属性.
    例如,如果喜欢的话,可以扩展一下日期:
    页面访问时间 : {^/HttpContext/Timestamp/LocalTimeHTML}


    TemplateEngine的常用指令:

    #include指令 - {#include 文件相对路径}
    这个指令很重要,因为它允许包含其他的文件.
    例如
    <!--{#include Header.htm}-->
    ....
    <!--{#include Footer.htm}-->

    #end指令
    当使用了#select,#if,#foreach这些block指令后,必须使用{#end}来表示结束.

    #select指令 - {#select XPathExp}
    这个指令用于跳转到某个对象,用于减少代码,和提高查询的性能:
    例如不用#select:
    Title:{=/Controller/Article/Title}
    User :<a href='{~/profile.aspx?UserId}={=/Controller/Article/User/UserID}'>{=/Controller/Article/User/UserName}</a>
    Content : {=/Controller/Article/Content}

    使用了#select之后:
    <!--{#select /Controller/Article/}-->
    Title:{=Title}
    <!--{#select User}-->
    User :<a href='{~/profile.aspx?UserId}={=UserID}'>{=UserName}</a>
    <!--{#end}-->
    Content : {=Content}
    <!--{#end}-->

    #select还有一个特点, 如果该对象找不到,那么就会跳过对应的{#end}里的内容.

    #if,#else,#elseif指令 - {#if XPathExp} {#else} {#elseif XPathExp}
    这些指令用于基本的条件判断.
    条件成立的情况是XPathExp存在着结果,并且第一个结果不是null或者bool的false
    例如
    <!--{#if /HttpContext/Request/QueryString/@HideDetails!='1'}-->
    <div class="details">{#include details.htm}</div>
    <!--{#end}-->

    #foreach指令
    这个指令用于循环集合(数组)或者字典.
    对于ICollection的实现,需要循环Item元素:
    <!--{#foreach /HttpContext/Request/UserLanguages/Item}-->
    {=.}
    <!--{#end}-->

    对于IDictionary类型的,则Item表示循环KeyValuePair:
    <!--{#foreach /HttpContext/Request/Cookies/Item}-->
    {=Key}={=Value}
    <!--{#end}-->

    当然可以直接循环Values:
    <!--{#foreach /HttpContext/Request/Cookies/Item/Value}-->
    {=Path}{=Name}
    <!--{#end}-->


    #continue , #break 指令
    用于中断#foreach指令的循环.

    #dir指令
    用于表示当前目录.
    例如
    <img src='{#dir}/images/icon.gif' />

    #design指令
    这个是一个block指令,用于插入设计时的代码,或者是注释 ,
    例如
    <!--{#design}-->
    <link rel=stylesheet href='design.css' />
    <!--{#end}-->

    #design指令的内容会直接忽略.

    #comment指令
    和#design用意一样, 不过这个不是block指令,不需要#end 例如
    <!--{#comment 客户端看不到的注释}-->


    扩展的指令:

    {#render 控件ID}
    这个指令直接把对应的控件的值Render出来.
    它是MVC的核心指令.
    因为它的存在, 让我的MVC完全兼容控件的生命周期, 并且完美地和ASP.NET AJAX兼容.


    {#loadascx Ascx文件}
    这个指令加载一个用户控件,并且把控件Render出来.


    TemplateEngine的基本用法就介绍到这里了.
    它还只是一个构造中的方案.
    我也正在拿它来做一些小项目,积累一些经验,然后改进它.

     
    展开全文
  • Spring Boot + Thymeleaf 报错:ERROR 8592 org.thymeleaf.TemplateEngine 报错详情 org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: “class path ...
  • velocity+xml解析的sql模板引擎,让复杂多条件的sql编写更简单、更快速 ???? 使用案例 在spring boot项目的resource目录下新建一个jpql文件夹,并在jpql文件夹下新建一个xml文件用来写sql语句。比如,这里我...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
    百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对...
  • 该模板带有各种脚本,材料,模型,以及完整的源代码,以防您希望比xml脚本更进一步地编辑菜单或使用C ++构建游戏。 特征 3.6准备好了! 具有背景图支持的有效菜单UI。 一个内置的加载屏幕系统,能够让地图调用...
  • 是一个索引引擎,它使用广度优先搜索索引给定的网页。 由于我缺乏让索引器 24/7 全天候运行的资金,因此我选择了一个解决方案,用户可以输入他们想要索引的链接并选择... 用户可以将搜索结果下载为 CSV、JSON 或 XML
  • 摘要: 项目中需要升级struts到struts2.5.14.1,这个版本有较大的改动,于是出现了较多的jar包冲突。很郁闷的是,很多问题在jetty服务器下,才会发生。...并且在项目的合适位置引入log4j2.xml的配置文件。关于
  • Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。 当Velocity应用于web开发时,界面设计人员可以和java程序...
  • 3.1_The template engine

    2015-04-02 07:48:37
    The template engine A type safe template engine based on Scala Play comes with Twirl, a powerful Scala-based template engine, whose design was inspired by ASP.NET Razor. Specifically it is: ...
  • 最近尝试ehcache3.x的无xml配置,在使用过程中遇到的很多坑,在网上也没有查到与之相关的文章很少,随后去查看官方文档测试之后的个人总结,如果有不对的地方还请指正。
  • pom.xml < parent > < groupId > org . springframework . boot < / groupId > < artifactId > spring - boot - starter - parent < / artifactId > < version > 2.2 .2 . RELEASE < / version > < ...
  • 红玛瑙 静态HTML | XML | 使用JSX的SVG渲染器,适合于报表输出。 RedAgate是静态HTML XML | SVG渲染器。... 捆绑了许多标准标签库(例如,If,Repeat,ForEach,Template,Html5,Svg,SVG形状,条形码(QR Co
  • UE4+XML文件读取

    2019-12-10 16:12:00
    XML文件的读取,对于应用开发,项目配置很是方便。 此帖将XML文件读取集成为UE4的插件,然后写了一个简单的工程,解释了实际项目开发中的使用。 XML文件读取插件,利用了Tinyxml来读取xml文件,给出他的官网地址:...
  • doxyrest:从Doxygen XML到reStructuredText的编译器-因此,是名称。 它解析Doxygen生成的XML数据库并为Python文档生成器Sphinx生成reStructuredText。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,777
精华内容 7,110
关键字:

enginetemplatexml