lucidquiet
lucidquiet

Reputation: 6558

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

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 (
    "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)
}

The output from running the code:

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

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?

Upvotes: 47

Views: 63306

Answers (4)

nemo
nemo

Reputation: 57609

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.

Upvotes: 31

infiniteLearner
infiniteLearner

Reputation: 4189

Another simple solution can be ,

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

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

Upvotes: 2

VonC
VonC

Reputation: 1323363

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.

Upvotes: 6

Luke
Luke

Reputation: 14128

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.

Upvotes: 6

Related Questions