基于变量的 Golang 格式文本模板
Golang Format Text Template based on variable
我正在尝试使用 text/template
即时生成类似 ini 的配置,其中原始数据以 yaml 格式提供。
我希望生成的输出根据请求的来源而有所不同。
请考虑以下损坏的代码:
package main
import (
"fmt"
"gopkg.in/yaml.v3"
"os"
"text/template"
)
var yamlData = `
# comment
---
States:
- StateName: California
DateEstablished: September 9, 1850
Cities:
- CityName: Los Angeles
Population: 4 Million
- CityName: Santa Barbara
Population: 92 Thousand
- CityName: San Jose
Population: 1 Million
- StateName: Washington
DateEstablished: November 11, 1889
Cities:
- CityName: Seattle
Population: 724 Thousand
Climate: wet
- CityName: Spokane
Population: 217 Thousand
Climate: dry
- CityName: Scappoose
Population: 7
`
const reportTemplate string = `{{ range . }}
# {{ if .CityName == requestingCityName }}
# {{ .CityName }}
[RequestingCity]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
# {{ if .CityName != requestingCityName }}
# {{ .CityName }}
[City]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
{{ end }}
`
type dataStruct struct {
States []struct {
StateName string `yaml:"StateName"`
DateEstablished string `yaml:"DateEstablished"`
Cities []struct {
CityName string `yaml:"CityName"`
Population string `yaml:"Population"`
Climate string `yaml:"Climate,omitempty"`
} `yaml:"Cities"`
} `yaml:"States"`
}
func (d *dataStruct) readData(data []byte) *dataStruct {
yaml.Unmarshal(data, d)
return d
}
func main() {
var report dataStruct
report.readData([]byte(yamlData))
t := template.Must(template.New("new").Parse(reportTemplate))
requestingStateName := "Washington"
requestingCityName := "Seattle"
for i := range report.States {
if report.States[i].StateName == requestingStateName {
x := report.States[i].Cities
fmt.Println(t.Execute(os.Stdout, x))
}
}
}
此代码的大部分 "works" 符合我的预期,但我遇到问题的部分是如何制作模板。
我希望能够更改 requestingCityName
的值,以便输出会像这样更改:
如果requestingCityName
== "Scappoose"
那么输出将是这样的:
# Scappoose
[RequestingCity]
Population = 7
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Seattle
[City]
Population = 724 Thousand
Climate = wet
或者如果 requestingCityName
== "Seattle"
那么输出将是这样的:
# Seattle
[RequestingCity]
Population = 724 Thousand
Climate = wet
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Scappoose
[City]
Population = 7
如何制作模板才能实现所需的行为?
一个简单的解决方案是将不同的数据对象传递给 Template.Execute
。类似于:
type templateData struct {
requestingCityName string
cities []citiesStruct // or whatever you name the struct
}
...
fmt.Println(t.Execute(os.Stdout, templateData{requestingCityName, x}))
此解决方案将要求您更新模板以使用新的上下文结构(即旧城市数组现在是 .cities
而不是 .
),但它使您可以访问.requestingCityName
.
您可以使用 eq
在模板中执行 if-condition,请参阅 。要否定 if 你可以写 if not <a> eq <b>
.
我以为你不在乎顺序。如果你关心你可以预先排序,所以在顶部是请求的城市和下面的所有其他内容。否则,您必须将 request-city-printing 拆分并输出到请求范围之外,然后在遍历所有城市后过滤掉请求的内容。
最干净的方法可能是在保存请求城市的数据结构中使用一个单独的属性,并预先从城市中过滤掉那个。
哦,如果您还没有将请求的城市添加到数据结构中,那么请事先进行。通过将 yaml 数据结构嵌入到名为 DataRendering
的结构中,该结构也具有请求的城市属性,这可能是最容易分离的。
我正在尝试使用 text/template
即时生成类似 ini 的配置,其中原始数据以 yaml 格式提供。
我希望生成的输出根据请求的来源而有所不同。
请考虑以下损坏的代码:
package main
import (
"fmt"
"gopkg.in/yaml.v3"
"os"
"text/template"
)
var yamlData = `
# comment
---
States:
- StateName: California
DateEstablished: September 9, 1850
Cities:
- CityName: Los Angeles
Population: 4 Million
- CityName: Santa Barbara
Population: 92 Thousand
- CityName: San Jose
Population: 1 Million
- StateName: Washington
DateEstablished: November 11, 1889
Cities:
- CityName: Seattle
Population: 724 Thousand
Climate: wet
- CityName: Spokane
Population: 217 Thousand
Climate: dry
- CityName: Scappoose
Population: 7
`
const reportTemplate string = `{{ range . }}
# {{ if .CityName == requestingCityName }}
# {{ .CityName }}
[RequestingCity]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
# {{ if .CityName != requestingCityName }}
# {{ .CityName }}
[City]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
{{ end }}
`
type dataStruct struct {
States []struct {
StateName string `yaml:"StateName"`
DateEstablished string `yaml:"DateEstablished"`
Cities []struct {
CityName string `yaml:"CityName"`
Population string `yaml:"Population"`
Climate string `yaml:"Climate,omitempty"`
} `yaml:"Cities"`
} `yaml:"States"`
}
func (d *dataStruct) readData(data []byte) *dataStruct {
yaml.Unmarshal(data, d)
return d
}
func main() {
var report dataStruct
report.readData([]byte(yamlData))
t := template.Must(template.New("new").Parse(reportTemplate))
requestingStateName := "Washington"
requestingCityName := "Seattle"
for i := range report.States {
if report.States[i].StateName == requestingStateName {
x := report.States[i].Cities
fmt.Println(t.Execute(os.Stdout, x))
}
}
}
此代码的大部分 "works" 符合我的预期,但我遇到问题的部分是如何制作模板。
我希望能够更改 requestingCityName
的值,以便输出会像这样更改:
如果requestingCityName
== "Scappoose"
那么输出将是这样的:
# Scappoose
[RequestingCity]
Population = 7
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Seattle
[City]
Population = 724 Thousand
Climate = wet
或者如果 requestingCityName
== "Seattle"
那么输出将是这样的:
# Seattle
[RequestingCity]
Population = 724 Thousand
Climate = wet
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Scappoose
[City]
Population = 7
如何制作模板才能实现所需的行为?
一个简单的解决方案是将不同的数据对象传递给 Template.Execute
。类似于:
type templateData struct {
requestingCityName string
cities []citiesStruct // or whatever you name the struct
}
...
fmt.Println(t.Execute(os.Stdout, templateData{requestingCityName, x}))
此解决方案将要求您更新模板以使用新的上下文结构(即旧城市数组现在是 .cities
而不是 .
),但它使您可以访问.requestingCityName
.
您可以使用 eq
在模板中执行 if-condition,请参阅 if not <a> eq <b>
.
我以为你不在乎顺序。如果你关心你可以预先排序,所以在顶部是请求的城市和下面的所有其他内容。否则,您必须将 request-city-printing 拆分并输出到请求范围之外,然后在遍历所有城市后过滤掉请求的内容。
最干净的方法可能是在保存请求城市的数据结构中使用一个单独的属性,并预先从城市中过滤掉那个。
哦,如果您还没有将请求的城市添加到数据结构中,那么请事先进行。通过将 yaml 数据结构嵌入到名为 DataRendering
的结构中,该结构也具有请求的城市属性,这可能是最容易分离的。