Golang模板处理和泛型

huangapple go评论143阅读模式
英文:

golang templates processing and generics

问题

我有两个golang的html模板,如下所示:

var m map[string]string
m = make(map[string]string)

m["First"] = `<html>
<body>First template type {{.First}}
</html>`

m["Second"] = `<html>
<body>Second template type {{.SecondF1}} {{.SecondF2}}
</html>`

第一个html模板只需要一个参数,名为First,而第二个模板需要两个参数,分别为SecondF1SecondF2

现在我有一个结构体,它有两个字段,一个用于接收模板名称,另一个用于接收模板参数。

type tmplReceiver struct {
	TmplName string
	TmplArgs string // 接收JSON字符串
}

上述结构体的实例示例可以是:

var i, j tmplReceiver

i.TmplName = "First"
i.TmplArgs = `{"Field1": "First Template Argument"}`

j.TmplName = "Second"
j.TmplArgs = `{
  "SecondF1": "Second template First Argument",
  "SecondF2": "Second template Second Argument"
}`

现在,我可以使用映射来获取模板字符串,例如:

tmplStr := m[i.TmplName] // 或者
tmplStr := m[j.TmplName]

然后,可以使用以下代码执行模板,以获取所有可能的模板类型:

var buff bytes.Buffer
if err := tmpl.Execute(&buff, tmplPtr); err != nil {
    log.Fatal(err)
}
log.Println(buff.String())

但是,无论我有多少个模板(FirstSecond等),以及每个模板可以有多少个参数(First只有一个参数,而Second有两个参数等),我如何使tmplPtr有效?

我不想为每个模板名称编写N个不同的tmpl.Execute语句,并带有一个if块。是否有其他替代方法来解决这个问题?谢谢。

英文:

I have two golang html templates, as follows:

var m map[string]string
m = make(map[string]string)

m[&quot;First&quot;] = `&lt;html&gt;
&lt;body&gt;First template type {{.First}}
&lt;/html&gt;`

m[&quot;Second&quot;] = `&lt;html&gt;
&lt;body&gt;Second template type {{.SecondF1}} {{.SecondF2}}
&lt;/html&gt;`

The first html template takes only one argument, named First whereas the second template needs two arguments, named SecondF1 and SecondF2.

Now I have a struct which has two fields, one for receiving a template name and another for receiving the template arguments.

type tmplReceiver struct {
	TmplName string
	TmplArgs string // Receives JSON string
}

Now, examples of instances for the above structs could be:

var i, j tmplReceiver

i.TmplName = &quot;First&quot;
i.TmplArgs = `{&quot;Field1&quot;: &quot;First Template Argument&quot;}`

j.TmplName = &quot;Second&quot;
j.TmplArgs = `{
  &quot;SecondF1&quot;: &quot;Second template First Argument&quot;, 
  &quot;SecondF2&quot;: &quot;Second template Second Argument&quot;
}`

Now I can get the Template string by using the map, for example:

tmplStr := m[i.TmplName] (or)
tmplStr := m[j.TmplName]

tmpl, _ = template.New(&quot;email&quot;).Parse(tmplStr)

However, how do I get the template to be executed for all the possible template types, with a single tmpl.Execute statement. In other words, if I want to have the following code:

var buff bytes.Buffer
if err := tmpl.Execute(&amp;buff, tmplPtr); err != nil {
	log.Fatal(err)
}
log.Println(buff.String())

How do I get the tmplPtr to be valid, irrespective of how many templates I have (First, Second, etc.) and each of these templates can have a variable number of arguments (First has only one arg, whereas Second has two args, etc.)

I do not want to write N different tmpl.Execute statements with an if block for each template name. Is there any other alternative approach to solve this ? Thanks.

答案1

得分: 0

json.Unmarshaltemplate.Execute都不关心数据的实际类型,这些都将在运行时处理。因此,你可以将JSON解析为interface{},然后将其传递给模板。只要JSON数据包含模板所需的字段,这将正常工作。

以下是翻译好的代码:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
)

var templates = map[string]*template.Template{
	"A": template.Must(template.New("A").Parse("{{ .A }}")),
	"B": template.Must(template.New("B").Parse("{{ .B }} and {{ .C.D }}")),
}

type tmplReceiver struct {
	TmplName string
	TmplArgs string
}

func main() {
	receivers := []tmplReceiver{
		tmplReceiver{"A", `{"A": "A的值"}`},
		tmplReceiver{"B", `{"B": "B的值", "C": { "D": "D的值" }}`},
	}

	for _, receiver := range receivers {
		var data interface{}
		json.Unmarshal([]byte(receiver.TmplArgs), &data)

		var buffer bytes.Buffer
		templates[receiver.TmplName].Execute(&buffer, data)
		fmt.Println(buffer.String())
	}
}

输出结果为:

A的值

B的值 and D的值

Playground链接

英文:

Neither json.Unmarshal nor template.Execute cares about the actual type of the data, this will all be handled at runtime. So you can just parse the json to an interface{} and pass that to your templates. Provided that the json data contains the fields that are expected by the template to which you pass the data, this will just work fine.

Playground link

package main

import (
    &quot;bytes&quot;
    &quot;encoding/json&quot;
    &quot;fmt&quot;
    &quot;html/template&quot;
)

var templates = map[string]*template.Template{
    &quot;A&quot;: template.Must(template.New(&quot;A&quot;).Parse(&quot;{{ .A }}&quot;)),
    &quot;B&quot;: template.Must(template.New(&quot;B&quot;).Parse(&quot;{{ .B }} and {{ .C.D }}&quot;)),
}

type tmplReceiver struct {
    TmplName string
    TmplArgs string
}

func main() {
    receivers := []tmplReceiver{
        tmplReceiver{&quot;A&quot;, `{&quot;A&quot;: &quot;Value for A&quot;}`},
        tmplReceiver{&quot;B&quot;, `{&quot;B&quot;: &quot;Value for B&quot;, &quot;C&quot;: { &quot;D&quot;: &quot;Value for D&quot; }}`},
    }

    for _, receiver := range receivers {
        var data interface{}
        json.Unmarshal([]byte(receiver.TmplArgs), &amp;data)

        var buffer bytes.Buffer
        templates[receiver.TmplName].Execute(&amp;buffer, data)
        fmt.Println(buffer.String())
    }
}

Which prints

> Value for A
>
> Value for B and Value for D

huangapple
  • 本文由 发表于 2017年9月5日 18:10:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/46052395.html
匿名

发表评论

匿名网友

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

确定