Golang get string representation of specific struct field name

huangapple go评论78阅读模式
英文:

Golang get string representation of specific struct field name

问题

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

假设我有一个结构体:

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

例如,我想要进行一个MongoDB的查找操作:

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

我不喜欢在这里放入字符串"OtherField"。它似乎很脆弱,容易出错,或者当结构体发生变化时,我的查询会失败而我却不知道。

有没有办法在不声明常量或类似的东西的情况下获取字符串"OtherField"?我知道可以使用反射从结构体中获取字段名列表,但我真的想做类似这样的事情:

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

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

英文:

I really want a way to print the string representation of a field name in go. It has several use cases, but here is an example:

lets say I have a struct

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

and, for example, I want to do a mongo find:

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

I don't like that I have to put the string "OtherField" in there. It seems brittle and easy to either misstype or have the struct change and then my query fails without me knowing it.

Is there any way to get the string "OtherField" without having to either declare a const or something like that? I know I can use reflection to a get a list of field names from a struct, but I'd really like to do something along the lines of

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

is there any way to do this in Go?? C# 6 has the built in nameof, but digging through reflection I can't find any way to do this in Go.

答案1

得分: 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.Typeinterface{}的数组,并输出一个代码文件。

  2. 在我的代码库中的某个地方创建一个generate.go文件,其中包含主函数:

    func main() {
        var text = mygenerator.Gen(Test{}, OtherStruct{}, ...)
        // 将文本写入constants.go或其他文件
    }
    
  3. 在我的主应用程序中添加//go:generate go run scripts/generate.go,然后运行go generate命令。

英文:

I don't really think there is. You may be able to load a set of types via reflection and generate a set of constants for the field names. So:

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

Could generate something like:

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

and you could use TestFields.Field as a constant.

Unfortunately, I don't know of any existing tool that does anything like that. Would be fairly simple to do, and wire up to go generate though.

EDIT:

How I'd generate it:

  1. Make a package that accepts an array of reflect.Type or interface{} and spits out a code file.

  2. Make a generate.go somewhere in my repo with main function:

     func main(){
        var text = mygenerator.Gen(Test{}, OtherStruct{}, ...)
        // write text to constants.go or something
     }
    
  3. Add //go:generate go run scripts/generate.go to my main app and run go generate

答案2

得分: 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) {
	// 用于查找未引用的json的正则表达式
	reg := regexp.MustCompile(`(\s*?{\s*?|\s*?,\s*?)(['"])?(?P<Field>[a-zA-Z0-9]+)(['"])?:`)

	// 以几乎json的形式打印结构体(字段未引用)
	raw := fmt.Sprintf("%#v", i)

	// 删除结构体名称,使字符串以"{"开头
	fjs := raw[strings.Index(raw,"{"):]

	// 查找并获取子匹配项3
	matches := reg.FindAllStringSubmatch(fjs,-1)

	// 收集字段
	fields := []string{}
	for _, v := range matches {
		if len(v) >= 3 && v[3] != "" {
			fields = append(fields, v[3])
		}
	}
	
	return fields, nil
}

希望对你有帮助!

英文:

Here is a function that will return a []string with the struct field names. I think it comes in the order they are defined.

WARNING: Reordering the fields in the struct definition will change the order in which they appear

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

package main

import (
	&quot;fmt&quot;
	&quot;strings&quot;
	&quot;regexp&quot;
	)

type Test struct {
    Field      string `bson:&quot;Field&quot; json:&quot;field&quot;`
    OtherField int    `bson:&quot;OtherField&quot; json:&quot;otherField&quot;`
}

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*?)([&#39;&quot;])?(?P&lt;Field&gt;[a-zA-Z0-9]+)([&#39;&quot;])?:`)
	
	// print struct in almost json form (fields unquoted)
	raw := fmt.Sprintf(&quot;%#v&quot;, i)
	
	// remove the struct name so string begins with &quot;{&quot;
	fjs := raw[strings.Index(raw,&quot;{&quot;):]
	
	// find and grab submatch 3
	matches := reg.FindAllStringSubmatch(fjs,-1)
	
	// collect
	fields := []string{}
	for _, v := range matches {
		if len(v) &gt;= 3 &amp;&amp; v[3] != &quot;&quot; {
			fields = append(fields, v[3])
		}
		
	}
	

return fields, nil

}

huangapple
  • 本文由 发表于 2017年5月5日 03:44:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/43791574.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定