如何从 golang 中的 reflect.Value 中获取底层值?

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

How to get underlying value from a reflect.Value in golang?

问题

我找到了一些帮助我开始在Go(golang)中使用反射的代码,但是我在获取底层值方面遇到了问题,以便我可以从结构体及其字段创建一个map[string]string

最终,我想将结果转换为map[string]interface{},但是这个问题阻碍了我。

我目前的代码如下:

package main

import (
	"fmt"
	"reflect"
)

type Foo struct {
	FirstName string `tag_name:"tag 1"`
	LastName  string `tag_name:"tag 2"`
	Age       int    `tag_name:"tag 3"`
}

func inspect(f interface{}) map[string]string {

	m := make(map[string]string)
	val := reflect.ValueOf(f).Elem()

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		f := valueField.Interface()
		val := reflect.ValueOf(f)
		m[typeField.Name] = val.String()
	}

	return m
}

func dump(m map[string]string) {

	for k, v := range m {
		fmt.Printf("%s : %s\n", k, v)
	}
}

func main() {
	f := &Foo{
		FirstName: "Drew",
		LastName:  "Olson",
		Age:       30,
	}

	a := inspect(f)

	dump(a)
}

运行代码的输出结果:

FirstName : Drew
LastName : Olson
Age : <int Value>

据我了解,FirstName和LastName的输出是实际的reflect.Value对象,但是对于字符串,值的String()方法只输出底层字符串。我想要获取int并将其更改为字符串,但是从reflect包的文档中,我并没有立即看到如何实现这一点。

那么...如何从reflect.Value中获取底层值呢?

英文:

So I found some code that help me get started with reflection in Go (golang), but I'm having trouble getting a the underlying value so that I can basically create a map[string]string from a struct and it's fields.

Eventually, I'd like to make the result into a map[string]interface{}, but this one issue is kind of blocking me.

The code I have at the moment:

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type Foo struct {
	FirstName string `tag_name:&quot;tag 1&quot;`
	LastName  string `tag_name:&quot;tag 2&quot;`
	Age       int  `tag_name:&quot;tag 3&quot;`
}

func inspect(f interface{}) map[string]string {

	m := make(map[string]string)
	val := reflect.ValueOf(f).Elem()

	for i := 0; i &lt; val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		f := valueField.Interface()
		val := reflect.ValueOf(f)
		m[typeField.Name] = val.String()
	}

	return m
}

func dump(m map[string]string) {

	for k, v := range m {
		fmt.Printf(&quot;%s : %s\n&quot;, k, v)
	}
}

func main() {
	f := &amp;Foo{
		FirstName: &quot;Drew&quot;,
		LastName:  &quot;Olson&quot;,
		Age:       30,
	}

	a := inspect(f)

	dump(a)
}

The output from running the code:

FirstName : Drew
LastName : Olson
Age : &lt;int Value&gt;

From what I understand the output for FirstName and LastName are actual reflect.Value objects but for strings the String() method on value just outputs the underlying String. I'd like to either get the int and change it into a string, but from the relfect package documentation I'm not immediately seeing how that's done.

Soo.... How do I get the underlying value from a reflect.Value in golang?

答案1

得分: 30

一个很好的解析值的示例是fmt包。请参考这段代码

使用上述代码来匹配你的问题,代码如下:

switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    m[typeField.Name] = strconv.FormatInt(val.Int(), 10)
case reflect.String:
    m[typeField.Name] = val.String()	
// 等等...
}

基本上,你需要检查所有可用的类型

英文:

A good example of how to parse values is the fmt package. See this code.

Using the mentioned code to match your problem would look like this:

switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    m[typeField.Name] = strconv.FormatInt(val.Int(), 10)
case reflect.String:
    m[typeField.Name] = val.String()	
// etc...
}

Basically you need to check for all available Kinds.

答案2

得分: 6

看起来你在正确的方向上。我看到你的代码存在一个问题,它对值做了一些假设,也就是说你何时调用Elem()以及调用多少次(用于解析指针)。为了知道这一点,你需要查看reflect.Kind。值是reflect.Ptr吗?那么就使用Elem()

一旦你从val.Interface() / val.String() / val.Int()中获取到值,你可以根据需要进行转换。你使用的方法将取决于reflect.Kind。要将int转换为string或从string转换为int,你需要使用strconv包。

encoding/jsonencoding/xml包已经提供了这种类型的工作。源代码中提供了一些很好的示例。例如,看一下encoding/xml/read.go中的copyValueencoding/xml/marshal.go中的marshalSimple

英文:

It looks like you're on the right track. The problem I see with your code is it makes assumptions about the values, meaning when do you call Elem() and how many times (to resolve pointers). In order to know this you need to look at the reflect.Kind. Is the value a reflect.Ptr? Then use Elem().

Once you have the value from val.Interface() / val.String() / val.Int() you can convert your values as needed. What you use is going to depend on reflect.Kind. To convert an int to/from string you need to use the strconv package.

The encoding/json and encoding/xml packages do this kind of work already. The source code provides some great examples. For example, take a look at copyValue in encoding/xml/read.go and marshalSimple in encoding/xml/marshal.go.

答案3

得分: 4

这在 Go 1.5(2015年8月)中应该更容易实现。
参见 review 8731commit 049b89dRob Pike (robpike) 提交的更改:

> ## fmt: 特殊处理 reflect.Value - 作为其所持有的值

这将允许您打印 Reflect.Value() 参数的实际值:

> 当将 reflect.Value 传递给 Printf(等等)时,fmt 调用了 String 方法,该方法不会透露其内容。
要获取内容,可以调用 Value.Interface(),但如果 Value 未导出或受其他限制,则此操作是非法的。

> 这个更改通过对 fmt 包进行微小的更改来改善情况:当我们将 reflect.Value 视为参数时,我们将其与在包内创建的 reflect.Value 完全相同对待。
这意味着我们始终打印 Value 的内容,就好像 那个 是传递给 Printf 的参数一样。

> 这可能是一个破坏性的更改,但我认为这是一个真正的改进,不会比我们对该包的格式化输出进行的许多其他微调更破坏。

英文:

This should be easier to do with Go 1.5 (August 2015)
See review 8731 and commit 049b89d by Rob Pike (robpike):

> ## fmt: treat reflect.Value specially - as the value it holds

This would allow you to print the actual value of a Reflect.Value() argument:

> When a reflect.Value is passed to Printf (etc.), fmt called the String method, which does not disclose its contents.
To get the contents, one could call Value.Interface(), but that is illegal
if the Value is not exported or otherwise forbidden.

> This CL improves the situation with a trivial change to the fmt package: when we see a reflect.Value as an argument, we treat it exactly as we treat a reflect.Value we make inside the package.
This means that we always print the contents of the Value as if that was the argument to Printf.

> This is arguably a breaking change but I think it is a genuine improvement and no greater a break than many other tweaks we have made to formatted output from this package.

答案4

得分: 2

另一个简单的解决方案可以是,

flavorName = fmt.Sprintf("%v", strct)

"fmt.Sprintf()"将返回可以存储在变量中的值。

英文:

Another simple solution can be ,

flavorName = fmt.Sprintf(&quot;%v&quot;,strct)

" fmt.Sprintf() " will return the value which can be stored in a variable.

huangapple
  • 本文由 发表于 2013年8月7日 06:23:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/18091562.html
匿名

发表评论

匿名网友

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

确定