Go by Example: Reflection

package main
import (
    "bytes"
    "fmt"
    "reflect"
    "strconv"
    "strings"
)

Part 1 - Law of reflections and Go reflect package basic operations Part 2 from 28:50 - Implementing a small JSON encoder using reflection

type User struct {
    Name string `en:"name" it:"nome"`
    Age  int64  `en:"age" it:"eta"`
}
type City struct {
    Name       string `en:"name" it:"nome"`
    Population int64  `en:"pop" it:"pop"`
    GDP        int64  `en:"gdp" it:"pil"`
    Mayor      string `en:"mayor" it:"sindaco"`
}
func main() {
    var x float64 = 3.14
    var u User = User{"bob", 10}

1st law: you can go from interface value to reflection obj

    refObjValPtr := reflect.ValueOf(&x)
    refObjtyp := reflect.TypeOf(&x)
    fmt.Printf("ref obj val for × %s\n", refObjValPtr.String())
    fmt.Printf("ref obj typ for × %s\n", refObjtyp.String())

2nd law: you can from reflection obj to interface value deference the ptr to its actual value

    refObjVal := refObjValPtr.Elem()
    fmt.Printf("ref obj val for float64 x: %f\n", refObjVal.Float())
    fmt.Printf("can set new val for refl obj? %v\n", refObjVal.CanSet())

3rd law when modifying reflection objects, not all of them can be settable

    refObjVal.Set(reflect.ValueOf(4.25))
    fmt.Printf("updated × using ptr and reflection obj, now val %f \n", x)
    res, err := JSONEncode(u, "en")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(res))
    c := City{"sf", 5000000, 567896, "mr jones"}
    res, err = JSONEncode(c, "it")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(res))
}
func JSONEncode(v interface{}, tagKey string) ([]byte, error) {
    refObjVal := reflect.ValueOf(v)
    refObjTyp := reflect.TypeOf(v)
    buf := bytes.Buffer{}
    if refObjVal.Kind() != reflect.Struct {
        return buf.Bytes(), fmt.Errorf(
            "val of kind %s is not supported",
            refObjVal.Kind(),
        )
    }
    buf.WriteString("{")
    pairs := []string{}
    for i := 0; i < refObjVal.NumField(); i++ {
        structFieldRefObj := refObjVal.Field(i)
        structFieldRefObjTyp := refObjTyp.Field(i)
        tag := structFieldRefObjTyp.Tag.Get(tagKey)
        switch structFieldRefObj.Kind() {
        case reflect.String:
            strVal := structFieldRefObj.Interface().(string)
            pairs = append(pairs, `"`+tag+`":"`+strVal+`"`)
        case reflect.Int64:
            intVal := structFieldRefObj.Interface().(int64)
            pairs = append(pairs, `"`+tag+`":`+strconv.FormatInt(intVal, 10))
        default:
            return buf.Bytes(), fmt.Errorf(
                "struct field with name %s and kind %s is not supprted",
                structFieldRefObjTyp.Name,
                structFieldRefObj.Kind(),
            )
        }
    }
    buf.WriteString(strings.Join(pairs, ","))
    buf.WriteString("}")
    return buf.Bytes(), nil
}

ref: The Go Blog: The Laws of Reflection

ref: Go (Golang) Reflection Tutorial

$ go run reflection.go
ref obj val for × <*float64 Value>
ref obj typ for × *float64
ref obj val for float64 x: 3.140000
can set new val for refl obj? true
updated × using ptr and reflection obj, now val 4.250000
{"name":"bob","age":10}
{"nome":"sf","pop":5000000,"pil":567896,"sindaco":"mr jones"}

Next example: Issue: Goroutine Leak.