8

我真的想要一种在 go 中打印字段名称的字符串表示形式的方法。它有几个用例,但这里有一个例子:

假设我有一个结构

type Test struct {
    Field      string `bson:"Field" json:"field"`
    OtherField int    `bson:"OtherField" json:"otherField"`
}

例如,我想做一个 mongo 查找:

collection.Find(bson.M{"OtherField": someValue})

我不喜欢我必须把字符串“OtherField”放在那里。错误输入或更改结构似乎很脆弱,然后我的查询在我不知情的情况下失败。

有什么方法可以得到字符串“OtherField”而不必声明一个常量或类似的东西?我知道我可以使用反射从结构中获取字段名称列表,但我真的很想做一些类似的事情

fieldName := nameOf(Test{}.OtherField) 
collection.Find(bson.M{fieldName: someValue})

有没有办法在 Go 中做到这一点?C# 6 有内置的 nameof,但是通过反射挖掘我在 Go 中找不到任何方法来做到这一点。

4

2 回答 2

7

我真的不认为有。您可以通过反射加载一组类型并为字段名称生成一组常量。所以:

type Test struct {
    Field      string `bson:"Field" json:"field"`
    OtherField int    `bson:"OtherField" json:"otherField"`
}

可以产生类似的东西:

var TestFields = struct{
      Field string
      OtherField string
}{"Field","OtherField"}

你可以TestFields.Field用作常数。

不幸的是,我不知道有任何现有的工具可以做这样的事情。做起来相当简单,但也可以连线go generate

编辑:

我将如何生成它:

  1. reflect.Type制作一个接受or数组interface{}并输出代码文件的包。
  2. generate.go在我的 repo 中创建一个带有 main 函数的地方:

    func main(){
       var text = mygenerator.Gen(Test{}, OtherStruct{}, ...)
       // write text to constants.go or something
    }
    
  3. 添加//go:generate go run scripts/generate.go到我的主应用程序并运行go generate
于 2017-05-04T19:54:55.850 回答
1

这是一个将返回[]string带有结构字段名称的函数。我认为它是按照它们定义的顺序出现的。

警告:重新排序结构定义中的字段将更改它们出现的顺序

https://play.golang.org/p/dNATzNn47S

package main

import (
    "fmt"
    "strings"
    "regexp"
    )

type Test struct {
    Field      string `bson:"Field" json:"field"`
    OtherField int    `bson:"OtherField" json:"otherField"`
}

func main() {
    fields, err := GetFieldNames(Test{})
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(fields)
}


func GetFieldNames(i interface{}) ([]string, error) {
    // regular expression to find the unquoted json
    reg := regexp.MustCompile(`(\s*?{\s*?|\s*?,\s*?)(['"])?(?P<Field>[a-zA-Z0-9]+)(['"])?:`)

    // print struct in almost json form (fields unquoted)
    raw := fmt.Sprintf("%#v", i)

    // remove the struct name so string begins with "{"
    fjs := raw[strings.Index(raw,"{"):]

    // find and grab submatch 3
    matches := reg.FindAllStringSubmatch(fjs,-1)

    // collect
    fields := []string{}
    for _, v := range matches {
        if len(v) >= 3 && v[3] != "" {
            fields = append(fields, v[3])
        }

    }


return fields, nil

}

于 2017-05-04T20:38:09.907 回答