【问题标题】:Golang Format Text Template based on variable基于变量的Golang格式文本模板
【发布时间】:2019-11-17 04:36:19
【问题描述】:

我正在尝试使用 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))

        }
    }
}

大部分代码都按照我期望的方式“工作”,但我遇到的问题是如何制作模板。

我希望能够更改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

如何制作模板以实现所需的行为?

【问题讨论】:

  • 使用两个范围,第一个只渲染请求的城市,第二个渲染所有其他的。或者在模板之外对切片进行排序。

标签: go yaml go-templates


【解决方案1】:

一个简单的解决方案是将不同的数据对象传递给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

【讨论】:

  • 这就是我所做的,制作了一个辅助结构,将我的数据解包到结构中,然后用它来填充模板。
【解决方案2】:

您可以使用 eq 在模板内执行 if 条件,请参阅 Compare strings in templates。否定一个如果你可以写if not <a> eq <b>

我以为你不关心订单。如果您在意,可以事先对其进行排序,因此顶部是请求的城市,下面是其他所有内容。否则,您必须将请求城市打印拆分并在请求范围之外输出,然后在覆盖所有城市后过滤掉请求。

最干净的方法可能是在请求城市所在的数据结构中使用一个单独的属性,并预先过滤掉那个城市。

哦,如果您还没有将请求的城市添加到数据结构中,那么请事先执行此操作。将 yaml 数据结构嵌入到一个名为 DataRendering 的结构中,这可能是最容易分离的方法,该结构也具有请求的城市属性。

【讨论】:

    猜你喜欢
    • 2016-12-02
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多