gostructreflect

Allow reflect to get unexported fields from a struct in Go


I am currently using reflect to get fields from a struct and return back the values as a slice of interface values. I've come across an issue with unexported fields, I want to be able to grab the unexported values and return them, along with the exported fields. When I try to get the values from my unexported fields I get the following error:

reflect.Value.Interface: cannot return value obtained from unexported field or method [recovered]

I have been using https://github.com/fatih/structs as a base for my code and want it to work with unexported fields.

// Values returns us the structs values ready to be converted into our repeatable digest.
func (s *StructWrapper) Values() []interface{} {
    fields := s.structFields()

    var t []interface{}

    for _, field := range fields {
        val := s.value.FieldByName(field.Name)
        if IsStruct(val.Interface()) {
            // look out for embedded structs, and convert them to a
            // []interface{} to be added to the final values slice
            t = append(t, Values(val.Interface())...)
        } else {
            t = append(t, val.Interface())
        }
    }

    return t
}

// Values converts the given struct to a []interface{}. For more info refer to
// StructWrapper types Values() method.  It panics if s's kind is not struct.
func Values(s interface{}) []interface{} {
    return New(s).Values()
}

// New returns a new *StructWrapper with the struct s. It panics if the s's kind is
// not struct.
func New(s interface{}) *StructWrapper {
    return &StructWrapper{
        raw:   s,
        value: strctVal(s),
    }
}

func strctVal(s interface{}) reflect.Value {
    v := reflect.ValueOf(s)

    // if pointer get the underlying element≤
    for v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    if v.Kind() != reflect.Struct {
        panic("not struct")
    }

    return v
}

// structFields returns the exported struct fields for a given s struct. This
// is a convenient helper method to avoid duplicate code in some of the
// functions.
func (s *StructWrapper) structFields() []reflect.StructField {
    t := s.value.Type()

    var f []reflect.StructField

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        f = append(f, field)
    }

    return f
}

I am getting the error when I call val.Interface() in my values method on an unexported field. Is there a way around this with reflect so it returns all exported and unexported field values?


Solution

  • Is there a way around this with reflect so it returns all exported and unexported field values?

    No.

    That would defeat the purpose of being unexported.