reflect.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. package formatter
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "reflect"
  6. "unicode"
  7. )
  8. func marshalJSON(x interface{}) ([]byte, error) {
  9. m, err := marshalMap(x)
  10. if err != nil {
  11. return nil, err
  12. }
  13. return json.Marshal(m)
  14. }
  15. // marshalMap marshals x to map[string]interface{}
  16. func marshalMap(x interface{}) (map[string]interface{}, error) {
  17. val := reflect.ValueOf(x)
  18. if val.Kind() != reflect.Ptr {
  19. return nil, fmt.Errorf("expected a pointer to a struct, got %v", val.Kind())
  20. }
  21. if val.IsNil() {
  22. return nil, fmt.Errorf("expxected a pointer to a struct, got nil pointer")
  23. }
  24. valElem := val.Elem()
  25. if valElem.Kind() != reflect.Struct {
  26. return nil, fmt.Errorf("expected a pointer to a struct, got a pointer to %v", valElem.Kind())
  27. }
  28. typ := val.Type()
  29. m := make(map[string]interface{})
  30. for i := 0; i < val.NumMethod(); i++ {
  31. k, v, err := marshalForMethod(typ.Method(i), val.Method(i))
  32. if err != nil {
  33. return nil, err
  34. }
  35. if k != "" {
  36. m[k] = v
  37. }
  38. }
  39. return m, nil
  40. }
  41. var unmarshallableNames = map[string]struct{}{"FullHeader": {}}
  42. // marshalForMethod returns the map key and the map value for marshalling the method.
  43. // It returns ("", nil, nil) for valid but non-marshallable parameter. (e.g. "unexportedFunc()")
  44. func marshalForMethod(typ reflect.Method, val reflect.Value) (string, interface{}, error) {
  45. if val.Kind() != reflect.Func {
  46. return "", nil, fmt.Errorf("expected func, got %v", val.Kind())
  47. }
  48. name, numIn, numOut := typ.Name, val.Type().NumIn(), val.Type().NumOut()
  49. _, blackListed := unmarshallableNames[name]
  50. // FIXME: In text/template, (numOut == 2) is marshallable,
  51. // if the type of the second param is error.
  52. marshallable := unicode.IsUpper(rune(name[0])) && !blackListed &&
  53. numIn == 0 && numOut == 1
  54. if !marshallable {
  55. return "", nil, nil
  56. }
  57. result := val.Call(make([]reflect.Value, numIn))
  58. intf := result[0].Interface()
  59. return name, intf, nil
  60. }