Текстовый шаблон формата Golang на основе переменной - PullRequest
3 голосов
/ 07 июля 2019

Я пытаюсь использовать 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

Как бы сделать шаблон таким образом, чтобыЯ могу добиться желаемого поведения?

Ответы [ 2 ]

3 голосов
/ 07 июля 2019

Одним простым решением было бы передать другой объект данных в 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.

1 голос
/ 07 июля 2019

Вы можете выполнить условие if внутри шаблона с помощью eq, см. Сравнить строки в шаблонах .Чтобы отрицать, если вы можете написать if not <a> eq <b>.

Я предполагал, что вы не заботитесь о порядке.Если вам не безразлично, вы можете отсортировать его заранее, поэтому вверху находится запрашиваемый город, а все остальное ниже.В противном случае вам нужно разделить запрос-город-печать и вывести его за пределы запрошенного диапазона, а затем отфильтровать запрошенный, как только вы охватите все города.

Самым чистым, вероятно, является использование отдельного атрибута.внутри структуры данных, в которой содержится запрашивающий город, и предварительно отфильтруйте этот из городов.

О, и если вы не добавили запрошенный город в структуру данных, сделайте это заранее.Вероятно, проще всего отделить, внедрив структуру данных yaml, вставив ее в структуру с именем DataRendering, которая также имеет запрошенный атрибут city.

...