猿问

在 Go 模板中按名称访问结构成员

我希望创建一个通用/通用的 Go html 模板,它将根据其输入生成一个标准的 html 表。我曾希望按名称查找结构成员,但我无法完成这项工作。


我环顾四周,找不到解决方案,所以我要么遗漏了一些明显的东西,要么方法是错误的。在这方面,我会接受一种解决方案,该解决方案显示了一种替代或更好的方法,可以避免尝试这种查找。


示例模板:


{{ $fields := .FieldMap }}

<table>

    <thead>

    <tr>

    {{ range $key, $value := $fields }}

        <th>{{ $key }}</th>

    {{ end }}

    </tr>


    </thead>

    <tbody>

    {{ range $i, $v :=  .Model }}

    <tr>

        {{ $rowData := . }}

{{/* FAILS: error calling index: can't index item of type main.Person  <td> {{ index . "FirstName"}}</td>*/}}

        {{ range $key, $value := $fields }}


{{/* FAILS: error calling index: can't index item of type main.Person   <td> {{ index $rowData $value }}</td>*/}}

{{/* FAILS: bad character U+0024 '$'                                    <td> {{ $rowData.$value }}</td>*/}}

        {{ end }}

    </tr>

    {{ end }}

    </tbody>

</table>

示例围棋:


包主


import (

    "html/template"

    "os"

)


type Person struct {

    FirstName string

    LastName string

}


type Animal struct {

    Species string

}


type TemplateData struct {

    Model interface{}

    FieldMap map[string]string

}


func main() {

    t, err := template.ParseFiles("table.gohtml")

    if err != nil {

        panic(err)

    }


    // Here we use Person, but I may want to pass other types of struct to the template, for example "Animal"

    dataPerson := TemplateData{

        Model: []Person{

            {

                FirstName: "Test",

                LastName:  "Template",

            },

        },

        FieldMap: map[string]string{"First": "FirstName", "Last": "LastName"},

    }


    err = t.Execute(os.Stdout, dataPerson)

    if err != nil {

        panic(err)

    }

}

我希望它清楚我想要做什么 - 有一个模板,我可以在各种类型的结构中重用它。


动漫人物
浏览 110回答 1
1回答

繁花不似锦

创建一个模板函数,将结构字段名称和值作为映射返回:// fields returns map of field names and values for struct s.func fields(s interface{}) (map[string]interface{}, error) {&nbsp; &nbsp; v := reflect.Indirect(reflect.ValueOf(s))&nbsp; &nbsp; if v.Kind() != reflect.Struct {&nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("%T is not a struct", s)&nbsp; &nbsp; }&nbsp; &nbsp; m := make(map[string]interface{})&nbsp; &nbsp; t := v.Type()&nbsp; &nbsp; for i := 0; i < t.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; sv := t.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; m[sv.Name] = v.Field(i).Interface()&nbsp; &nbsp; }&nbsp; &nbsp; return m, nil}解析文件时指定函数:t, err := template.New("").Funcs(template.FuncMap{"fields": fields}).ParseFiles("table.gohtml")if err != nil {&nbsp; &nbsp; panic(err)}像这样使用它:{{range $i, $v :=&nbsp; .Model}}<tr>&nbsp; &nbsp; {{$m := fields $v}}&nbsp; &nbsp; {{range $key, $value := $fields}}&nbsp; &nbsp; &nbsp; &nbsp;<td>{{index $m $value}}</td>&nbsp; &nbsp; {{end}}</tr>{{end}}在操场上运行它。另一种方法是编写一个按名称查找字段的函数:func field(s interface{}, k string) (interface{}, error) {&nbsp; &nbsp; v := reflect.Indirect(reflect.ValueOf(s))&nbsp; &nbsp; if v.Kind() != reflect.Struct {&nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("%T is not a struct", s)&nbsp; &nbsp; }&nbsp; &nbsp; v = v.FieldByName(k)&nbsp; &nbsp; if !v.IsValid() {&nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("no field in %T with name %s", s, k)&nbsp; &nbsp; }&nbsp; &nbsp; return v.Interface(), nil}用函数解析:t, err := template.New("").Funcs(template.FuncMap{"field": field}).ParseFiles("table.gohtml")像这样使用它:{{range $i, $v :=&nbsp; .Model}}<tr>&nbsp; &nbsp; {{range $key, $value := $fields}}&nbsp; &nbsp; &nbsp; &nbsp;<td>{{field $v $value}}</td>&nbsp; &nbsp; {{end}}</tr>{{end}}在操场上运行它。
随时随地看视频慕课网APP

相关分类

Go
我要回答