Join range block in go template

五迷三道 提交于 2019-12-08 07:14:54

问题


I have a go template like this:

 "environment": [
   {{- range $k,$v := .env }}
     {
       "name": "{{ $k }}",
       "value": "{{ $v }}"
     },
   {{- end }}
   ]

and i am getting the output below:

     "environment": [
    {
      "name": "name",
      "value": "test"
    },
    {
      "name": "region",
      "value": "us-east-1"
    },
  ]

and i want to render it like below:

    "environment": [
    {
      "name": "name",
      "value": "bxbd"
    },
    {
      "name": "region",
      "value": "us-east-1"
    }
  ]

I am not able to get rid of the last comma to make valid json. Or is it possible to somehow send the complete range block to some custom join function?


回答1:


Here's an example how to do it with templates, but I strongly recommend to use the 2nd approach if you want to generate JSON.

Sticking with templates

Since you're ranging over a map, you can't (simply) do it. In case of slices you could check the index variable (examples: Go template remove the last comma in range loop; and detect last item inside an array using range inside go-templates), but in case of maps you can't do that.

Knowing if you're at the first (or last) iteration is a state which you must maintain yourself. And example is to use custom functions or methods for this.

Here's an example implementation:

type Params struct {
    Env     map[string]string
    Counter int
}

func (p *Params) IncMore() bool {
    p.Counter++
    return p.Counter < len(p.Env)
}

const src = `"environment": [
   {{- range $k,$v := .Env }}
     {
       "name": "{{ $k }}",
       "value": "{{ $v }}"
     }{{if $.IncMore}},{{end}}
   {{- end }}
   ]`

Testing it:

func main() {
    t := template.Must(template.New("").Parse(src))
    p := &Params{
        Env: map[string]string{
            "name":   "test",
            "region": "us-east-1",
        },
    }
    err := t.Execute(os.Stdout, p)
    if err != nil {
        panic(err)
    }
}

Output (try it on the Go Playground):

"environment": [
     {
       "name": "name",
       "value": "test"
     },
     {
       "name": "region",
       "value": "us-east-1"
     }
   ]

Use encoding/json to generate JSON

If you're goal is to generate JSON, you should use the encoding/json package to generate valid JSON document. The above template has no knowledge about JSON syntax and context, and the values of the map entries are not escaped when written to the output, so you may still end up with invalid JSON.

Best would be to generate the JSON like this:

type Entry struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

type Params struct {
    Env []Entry `json:"environment"`
}

func main() {
    enc := json.NewEncoder(os.Stdout)
    enc.SetIndent("", "  ") // Optional
    p := &Params{
        Env: []Entry{
            {Name: "name", Value: "test"},
            {Name: "region", Value: "us-east-1"},
        },
    }
    err := enc.Encode(p)
    if err != nil {
        panic(err)
    }
}

Output (try it on the Go Playground):

{
  "environment": [
    {
      "name": "name",
      "value": "test"
    },
    {
      "name": "region",
      "value": "us-east-1"
    }
  ]
}


来源:https://stackoverflow.com/questions/51043433/join-range-block-in-go-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!