package encoding import ( "fmt" "reflect" "strings" ) func stringifyType(t reflect.Type) reflect.Type { switch t.Kind() { case reflect.Map: return reflect.MapOf(stringType, interfaceType) case reflect.Slice: return reflect.SliceOf(interfaceType) case reflect.Struct: return stringifyStructType(t) case reflect.Ptr: return stringifyType(t.Elem()) default: return stringType } } func stringifyStructType(t reflect.Type) reflect.Type { fields := make([]reflect.StructField, t.NumField()) for i := 0; i < t.NumField(); i++ { f := t.Field(i) fields[i] = reflect.StructField{ Name: f.Name, Type: stringifyType(f.Type), Tag: f.Tag, } } return reflect.StructOf(fields) } // Stringify converts any supported type into a stringified value func Stringify(v interface{}) (interface{}, error) { var err error if v == nil { return nil, nil } val := reflect.ValueOf(v) switch val.Kind() { case reflect.String, reflect.Bool, reflect.Int, reflect.Float64: return fmt.Sprint(v), nil case reflect.Map: out := make(map[string]interface{}) for _, key := range val.MapKeys() { v, err := Stringify(val.MapIndex(key).Interface()) switch { case err != nil: return nil, err case v != nil: out[key.String()] = v } } return out, nil case reflect.Slice: out := make([]interface{}, val.Len()) for i := 0; i < val.Len(); i++ { v, err = Stringify(val.Index(i).Interface()) switch { case err != nil: return nil, err case v != nil: out[i] = v } } return out, nil case reflect.Struct: t := val.Type() out := reflect.New(stringifyStructType(t)).Elem() for i := 0; i < t.NumField(); i++ { f := t.Field(i) v := val.FieldByName(f.Name) if tag, ok := f.Tag.Lookup("json"); ok { if strings.Contains(tag, ",omitempty") { if v.IsZero() { continue } } } s, err := Stringify(v.Interface()) switch { case err != nil: return nil, err case s != nil: out.Field(i).Set(reflect.ValueOf(s)) } } return out.Interface(), nil case reflect.Ptr: if val.IsNil() { return nil, nil } return Stringify(val.Elem().Interface()) } return nil, fmt.Errorf("Unsupported type: '%v'", val.Kind()) }